diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000000..e69de29bb2 diff --git a/404.html b/404.html new file mode 100644 index 0000000000..1d211d5fc4 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ + Tasmota
\ No newline at end of file diff --git a/A4988-Stepper-Motor-Controller/index.html b/A4988-Stepper-Motor-Controller/index.html new file mode 100644 index 0000000000..b7ee5e6d98 --- /dev/null +++ b/A4988-Stepper-Motor-Controller/index.html @@ -0,0 +1,10 @@ + A4988 Stepper Motor Controller - Tasmota
Skip to content

A4988 Stepper Motor Controller

This feature is not included in precompiled binaries

When compiling your build add the following to user_config_override.h:

#ifndef USE_A4988_STEPPER
+#define USE_A4988_STEPPER    // A4988/DRV8825 stepper motor (+10k5 code)
+#endif
+

This driver is used to control stepper-motors such as NEMA 17.

Configuration~

Wiring~

The driverboard has several connectors: powering the controller (3.3-5.0 V), input (+/-) & output (1a/1b/2a/2b), the motor (up to 35V/2A), and to control the circuit (in order at the control side of the board):

Connector Description
DIR Direction of rotation
STEP Initiate stepping
MS1 Microstep increment select
MS2 Microstep increment select
MS3 Microstep increment select
EN Enable the power supply for the motor
SLP Sleep (bridge to RST)
RST Reset (bridge to SLP)

Tasmota Settings~

There are six GPIO components that should be configured to free GPIOs:

A4988 DIR (170)  
+A4988 STP (171)  
+A4988 ENA (172)  
+A4988 MS1 (173)  
+A4988 MS2 (174)  
+A4988 MS3 (175)
+
The minimal configuration are the DIR and STEP signals. In such a configuration the motor will be permanently powered and microstepping will be set to 1/1 (full steps).

A4988 Controller~

Detailed information about the A4988 controller can be found in the datasheet.

Microstepping Configuration~

A4988-Truth-Table

DRV8825 Controller~

The DRV8825 is directly pin compatible with the A4988. The microstepping increment settings are different. Also, there is one additional option on the DRV8825.

Microstepping Configuration~

DRV8825 -Truth-Table

Operation~

Refer to the Stepper Motor Commands

MotorRPM is an imprecise setting due to the implementation method. Also, if the value is too high for the combination of chosen micro stepping increment (MotorMIS) and the number of steps the given motor needs for one revolution (MotorSPR), the motor will not turn but make a whining noise. You will have to experiment some to find the optimal combination for your use case.

Example Project~

The cheap auto-feeder for my cats broke. It was a fancy plastic-thingy with voice-recording-function & programmable to feed several times a day after playing back your voice (cats don't give a sh$7 about your voice - they come when they hear the food falling into the bowl). It was never precise - a concern for the amount of nutrition it gave the cats. And it was not reliable, as the torque of the internal moving mechanism was insufficient to spin the separator/proportioning wheel through the food reliably. In addition, the batteries were always drained in a day meaning very grumpy cats when we returned!

Thus the wish to install a high-torque stepper-motor (with shifting gear) was born. I could power it with mains instead of relying on a battery, control it over WiFi from my home automation hub. Tasmota now offers a way to do this!

The "TasmotaSmartCatFeeder" circuit consists of a WeMos D1 mini, an A4988 controller, and two power supplies (5V&12V). This all fits into the housing of the feeder and costs less than 50€!

Virtually everything which has to be moved or rotated can be done now using these cheap components. It can be a window, door, shutter, cat or dog flap, a solar panel which follows the sun, a moving spotlight, PTZ-camera, or whatever.

Wiring Diagrams~

Nema-17_v1 28BYJ-48_V1

Convert 28BYJ-48 to bipolar so that it works with this driver

Breakout Boards~

Banggood
AliExpress #1
AliExpress #1

\ No newline at end of file diff --git a/ADC/index.html b/ADC/index.html new file mode 100644 index 0000000000..f29a464f1f --- /dev/null +++ b/ADC/index.html @@ -0,0 +1,5 @@ + Analog Pin - Tasmota
Skip to content

Analog Pin

The ESP8266 has a single ADC pin available. It may be used to read voltage at the ADC pin or to read a module supply voltage (VCC).

Note

When referring to the ADC pin these terms are used interchangeably: ADC (Analog-to-digital Converter), TOUT, Pin6, A0 or Analog0.

Warning

Check your Wi-Fi module! The ESP8266 A0 pin supports a maximum voltage of 1.0V. Many newer Wi-Fi modules have an on-board voltage divider to support a higher A0 input voltage range (typically in the range between 0 and 3.3 volts). You may need to use an external voltage divider to ensure your input voltage is in the right range.

By default Tasmota uses the ADC pin to read voltage. The signal comes from an analog peripheral, or sometimes from the device itself (see Shelly 2.5).

After wiring a peripheral to GPIO17 (A0) pin you have to configure it in Configure Module:

ADC configuration

The ESP32 board has four ADC GPIO pins available (gpio34, gpio35, gpio36, gpio39) which can be used for analog inputs. These can be used to read voltage at the ADC pin or to read the module supply voltage (VCC). All analog input pins support 3.3V.

After wiring a peripheral to the specified Analog Input GPIO pin, it can be configured in Configure Module:

ADC configuration

# Option WebUI display MQTT message
0 None none none
1 Analog Analog0 %value% {"A0":%value%}
2 Temperature Temperature %value% °C (°F) {"Temperature":%value%},"TempUnit":"C"}
3 Light Illuminance %value% lux {"Illuminance":%value%}
4 Button none none
5 Buttoni none none
6 Range Range %value% {"Range":%value%}
7 CT Power Voltage 230 V
Current %value A
Power %value W
Energy Total %value kWh
{"Energy":%value,"Power":%value,"Voltage":230,"Current":%value}
9 pH ph %value {"pH":%value}
10 MQ-X MQ-X %value ppm {"MQX":%value}

The reading will show in the web UI's sensor section as "%option% %value%" depending on the selected option. Tasmota calculates the values for temperature and light, analog values can be 1 to 1024.

Note

When using Temperature (2) or light (3) calibration may be required. AdcParam can be used to calibrate the output to adjust for any shifted values.

Example: ADC as Analog (1)
ADC in web UI

A message will be published in tele/%topic%/SENSOR JSON response as "ANALOG": depending on the selected option.

Example: ADC as Light (3)

18:55:09 MQT: tele/tasmota/SENSOR = {"Time":"2019-10-31T18:55:09","ANALOG":{"Illuminance":8}}
+

Warning

Be careful when setting ADC as a 'Button', if there is a constant voltage to the pin it might register as a long press and reset the device to firmware defaults.

Commands~

Command Parameters
AdcParam<x> ADC analog input tuning parameters. On ESP32 x is channel 1..8, and the input max is 4095 instead of 1023 as used below.
<sensor>, <param1>, <param2>, <param3>, <param4>
<sensor> values:
2 = Temperature Steinhart-Hart thermistor equation parameters:
    <param1> = NTC Voltage bridge resistor in Ohms (default = 32000)
    <param2> = NTC Resistance in Ohms (default = 10000)
    <param3> = NTC Beta Coefficient (default = 3350)

3 = Light Lux equation parameters:
    <param1> = LDR Voltage bridge resistor in Ohms (default = 10000)
    <param2> = LDR Lux Scalar (default = 12518931)
    <param3> = LDR Lux Exponent (default = -1.4050)

6 = ADC linear range remapping parameters:
    <param1> = input range low value adcLow (default = 0)
    <param2> = input range high value adcHigh (default = 1023)
    <param3> = output range low value rangeLow (default = 0)
    <param4> = output range high value rangeHigh (default = 100)
    The range remapping perform the following calculation on the ADC value [0..1023]:
    Range = ((adcHigh - ADC) / (adcHigh - adcLow)) * (rangeLow - rangeHigh) + rangeHigh
    The calculation is performed in double resolution floating point, but the output is a signed integer. All 4 input parameters are unsigned 16 bit integers.
    Example to convert the ADC value on a D1-mini into millivolts (using the default resistor bridge of 220k/100k):
    AdcParam 6, 0, 1023, 0, 3200

7 = CT POWER parameter adjustments:
    <param1> = ANALOG_CT_FLAGS (default 0 for a non-invasive current sensor). When value is >0 it sets the adcLow value as base for the measurement via OpAmp differential amplifier.
    <param2> = ANALOG_CT_MULTIPLIER ( 2146 = Default settings for a (AC) 20A/1V Current Transformer.) multiplier*100000 to convert raw ADC peak to peak range 0..1023 to RMS current in Amps. Value of 100000 corresponds to 1
    <param3> = ANALOG_CT_VOLTAGE (default 2300) to convert current in Amps to apparent power in Watts using voltage in Volts/1000. Use value 0.220 for AC220V or 0.012 for 12VDC.
    <param4> minimum current threshold (AC only, optional, default 0). Current measured below this value will be forced to 0.0. Used to void measurment noise when load is null.
    Exemple for DC: AdcParam 7,406,3282,0.012
    Exemple for AC: AdcParam 7,0,2146,0.230,0.060
    See circuit diagram below

9 = ANALOG_PH parameter adjustments:
    <param1> = ANALOG_PH_CALSOLUTION_LOW_PH (default 4.0).
    <param2> = ANALOG_PH_CALSOLUTION_LOW_ANALOG_VALUE ( default 282 )
    <param3> = ANALOG_PH_CALSOLUTION_HIGH_PH (default 9.18).
    <param4> = ANALOG_PH_CALSOLUTION_HIGH_ANALOG_VALUE (default 435).

    To calibrate the probe, two reference solutions with known pH are required. Calibration procedure:
    1. Put probe in solution with lower pH value. pH value of the solution is ANALOG_PH_CALSOLUTION_LOW_PH.
    2. Wait until analog value / RAW value stabilizes (~3 minutes)
    3. The analog reading is ANALOG_PH_CALSOLUTION_LOW_ANALOG_VALUE
    4. Clean probe and put in solution with higher pH value. pH value of the solution is ANALOG_PH_CALSOLUTION_HIGH_PH.
    5. Wait until analog value / RAW value stabilizes (~3 minutes)
    6. The analog reading is ANALOG_PH_CALSOLUTION_HIGH_ANALOG_VALUE
    Analog readings can be read by either changing the analog port configuration to "Analog Input" while calibrating, or by enabling debug logs in the console and having a look at the RAW Valuereading instead.

10 = MQ-X sensors parameter adjustments:
    <param1> = ANALOG_MQ_TYPE (default 2) It used to specify sensor type. At the moment exists: 2, 3, 4, 5, 6, 7, 8, 9, 131, 135 (means MQ-02, MQ-03, MQ-04 ecc.).
    <param2> = ANALOG_MQ_A (default 574.25 a params for MQ-02) It is exponential regression a params
    <param3> = ANALOG_MQ_B (default -2.222 b params for MQ-02) It is exponential regression b params, generally negative
    <param4> = ANALOG_MQ_RatioMQCleanAir (default 15.0 RatioMQCleanAir params for MQ-02) NOT USED YET. It is threshold for good air in ppm for future alams arming
    Usage example for MQ-02, MQ-04, MQ-07 and MQ-131
    AdcParam 10, 2.00, 574.25, -2.22, 9.83
    AdcParam 10, 4.00, 1012.70, -2.79, 4.40
    AdcParam 10, 7.00, 99.04, -1.52, 27.50
    AdcParam 10, 131.00, 23.94, -1.11, 15.00

Rule triggers~

Use these triggers in rules:

on ANALOG#A0div10 do ... - when the ADC input changes by more than 1% it provides a value between 0 and 100

on Tele-ANALOG#A0 do ... - triggers on tele messages with Analog object

MQT: tele/tasmota/SENSOR = {"Time":"2019-01-14T19:36:51","ANALOG":{"A0":1024}}

Rule example: using a potentiometer on analog pin.

ADC_CT circuit~

You should use a current transformer with voltage output such as a 20A/1V or 50/1V. Do not use a CT with current output.

The below circuit shows how to connect the CT. The exact values for the capacitors or the resitors is not very critical (values that are double or half will work the same but the 2 resitors should be the same value).

In order to avoid noise, build as compact as possible using short wires and insulating where possible.

ADC_CT circuit

Note

The section on the right represents the voltage divider that is present on the NodeMCU or mini-D1 to provide a [0.0 - 3.3V] input range (the native range of ESP8266 ADC pin is 0.0-1.0V). If you are using a bare ESP8266 module without that divider, you need to add it.

Note

ESP32 natively has a [0.0 - 3.3V] input range and doesn't need the divider on the right.

ADC_VCC~

Instead of an input, the ADC pin can be used to measure the supply voltage of the ESP module (this reading is not 100% accurate). To enable the ADC_VCC feature; compile your own build:

If you enable ADC_VCC you cannot use the pin as analog input anymore.

user_config_override.h flag:

// -- Internal Analog input -----------------------
+#define USE_ADC_VCC                              // Display Vcc in Power status
+

Supply voltage is published in tele/%topic%/STATE under "Vcc": in mV:

11:14:59 MQT: tele/tasmota/STATE = {"Time":"2019-10-31T11:14:59","Uptime":"0T18:36:12","UptimeSec":66972,"Vcc":3.423,"Heap":28,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"MqttCount":6,"POWER":"OFF","Wifi":{"AP":1,"SSId":"Tasmota","BSSId":"00:00:00:00:00:00","Channel":13,"RSSI":100,"LinkCount":1,"Downtime":"0T00:00:06"}}
+

\ No newline at end of file diff --git a/AHT1x/index.html b/AHT1x/index.html new file mode 100644 index 0000000000..14d1f2cf59 --- /dev/null +++ b/AHT1x/index.html @@ -0,0 +1,5 @@ + AHT10/AHT15 temperature and humidity sensor - Tasmota
Skip to content

AHT10/AHT15 temperature and humidity sensor~

This feature is not included in precompiled binaries

When compiling your build add the following to user_config_override.h:

#ifndef USE_AHT1x
+#define USE_AHT1x       // [I2cDriver43] Enable AHT10/15 humidity and temperature sensor (I2C address 0x38, 0x39) (+0k8 code)
+#endif
+

AHT1x are an I2C temperature and humidity sensor. AHT2x or AM2301B are upgraded versions.

Configuration~

This sensor is incompatible with other I2C devices on I2C bus

Sensor datasheet implicitly says: Only a single AHT10 can be connected to the I2C bus and no other I2C devices can be connected. The AHT2x/AM2301B does not suffer from this problem.

Wiring~

AHT ESP
VCC 3.3V
GND GND
SDA GPIOx
SCL GPIOy

Tasmota Settings~

In the Configuration -> Configure Module page assign:

  1. GPIOx to I2C SDA
  2. GPIOy to I2C SCL

After a reboot the driver will detect AHT1x automatically and display sensor readings.

webUI readout

and in MQTT topic (according to TelePeriod):

{"Time":"2020-01-01T00:00:00","AHT1X-0x38":{"Temperature":24.7,"Humidity":61.9,"DewPoint":16.8},"TempUnit":"C"}
+

Troubleshooting~

On some I2C sensors, like the AHT25, Tasmota might mistake the sensor for another one using the same address. This can be resolved by disabling the unneeded drivers.

For our example AHT25, the address might get confused with the VEML6070 UV sensor. You can tell Tasmota to use the other driver by issuing the following commands in the console:

  • I2CDriver12 0 to disable the VEML driver
  • I2CDriver43 1 to enable the AHT driver

An overview of all I2C drivers is available in the docs.

Breakout Boards~

AHT10AHT15

Note: The pins on the smaller breakout board with the AHT15 are in a different order: VDD - SDA - GND - SCL See also the datasheet.

AHT10 Datasheet
AHT15 Datasheet

\ No newline at end of file diff --git a/AHT2x/index.html b/AHT2x/index.html new file mode 100644 index 0000000000..c144ab3d44 --- /dev/null +++ b/AHT2x/index.html @@ -0,0 +1,4 @@ + AHT2x/AM2301B temperature and humidity sensor - Tasmota
Skip to content

AHT2x/AM2301B temperature and humidity sensor~

This feature is not included in precompiled binaries

When compiling your build add the following to user_config_override.h:

#ifndef USE_AHT2x
+#define USE_AHT2x       // [I2cDriver43] Enable AHT20/AM2301B instead of AHT1x humidity and temperature sensor (I2C address 0x38) (+0k8 code)
+#endif
+

Configuration~

Wiring~

AHT ESP
VCC 3.3V
GND GND
SDA GPIOx
SCL GPIOy

AHT25 breakout boards require external pull-up resistors on the I2C bus (for example: 4.7kΩ)

Tasmota Settings~

In the Configuration -> Configure Module page assign:

  1. GPIOx to I2C SDA
  2. GPIOy to I2C SCL

After a reboot the driver should detect the sensor and display sensor readings.

Troubleshooting~

On some I2C sensors, like the AHT25, Tasmota might mistake the sensor for another one using the same address. This can be resolved by disabling the unneeded drivers.

For our example AHT25, the address might get confused with the VEML6070 UV sensor. You can tell Tasmota to use the other driver by issuing the following commands in the console:

  • I2CDriver12 0 to disable the VEML driver
  • I2CDriver43 1 to enable the AHT driver

An overview of all I2C drivers is available in the docs.

Breakout Boards~

AHT20AHT21AHT25AM2301B

AHT20 Datasheet
AHT21 Datasheet AHT25 Datasheet

\ No newline at end of file diff --git a/AM2301/index.html b/AM2301/index.html new file mode 100644 index 0000000000..c0a385c183 --- /dev/null +++ b/AM2301/index.html @@ -0,0 +1,12 @@ + AM2301/AM2302 (DHT21/DHT22) temperature and humidity sensor - Tasmota
Skip to content

AM2301/AM2302 (DHT21/DHT22) temperature and humidity sensor~

This feature is included in tasmota, tasmota32, tasmota-knx and tasmota-display binaries

When compiling your build add the following to user_config_override.h:

#ifndef USE_DHT
+#define USE_DHT     // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor (1k6 code)
+#endif
+

AM2301 driver supports AM2301 (DHT21), AM2302 (DHT22), AM2320 and AM2321 temperature and humidity sensors. Introduced in Tasmota through Sonoff AM2301 accessory for Sonoff TH.

This driver is ONLY for single wire implementations of the sensor.

For AM2301B I2C model, refer to AHT2x driver.

Configuration~

Wiring~

AM2301 ESP
- GND
OUT GPIOx
+ 3.3V - 5.2V (5V is recommended)
AM2320 ESP
- GND
OUT (SDA) GPIOx
+ 3.3V - 5.2V (5V is recommended)
SCL GND

Tasmota Settings~

In the Configuration -> Configure Module page assign: - GPIOx to AM2301

After a reboot webUI will display temperature and humidity measured.

image

Sensor sends a tele/%topic%/SENSOR JSON response:

{
+  "Time": "2019-01-01T00:00:00",
+  "AM2301": {
+    "Temperature": 24.6,
+    "Humidity": 58.2
+  },
+  "TempUnit": "C"
+}
+

Commands~

TempOffset can be used for calibrating the measured temperature. This setting affects all temperature sensors on the device.

Wemos DHT Shield~

Like the Wemos DHT11 shield specs the DATA OUT pin of Wemos DHT22 is connected to D4 of the Wemos.

image

Tasmota Settings~

In the Configuration -> Configure Module page assign:

  1. GPIO2 to AM2301 (2)

Sensors~

Read more about differences between sensors.

image

\ No newline at end of file diff --git a/APDS-9960/index.html b/APDS-9960/index.html new file mode 100644 index 0000000000..a4078d17ab --- /dev/null +++ b/APDS-9960/index.html @@ -0,0 +1,20 @@ + APDS-9960 light and gesture sensor - Tasmota
Skip to content

APDS-9960 light and gesture sensor~

This feature is not included in precompiled binaries

When compiling your build add the following to user_config_override.h:

#ifndef USE_APDS9960
+#define USE_APDS9960                // [I2cDriver21] Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
+  #define USE_APDS9960_GESTURE        // Enable APDS9960 Gesture feature (+2k code)
+  #define USE_APDS9960_PROXIMITY      // Enable APDS9960 Proximity feature (>50 code)
+  #define USE_APDS9960_COLOR          // Enable APDS9960 Color feature (+0.8k code)
+  #define USE_APDS9960_STARTMODE  0   // Default to enable Gesture mode
+#endif
+

Broadcom APDS-9960 is a digital RGB, ambient light, proximity and gesture sensor device in a single 8-pin package. The device has an I2C compatible interface providing red, green, blue, clear (RGBC), proximity and gesture sensing with IR LED. The RGB and ambient light sensing feature detects light intensity under various lighting conditions and through various attentuation materials including darkened glass. In addition, the integrated UV-IR blocking filter enables accurate ambient light and correlated color temperature sensing.

Configuration~

Wiring~

Breakout ESP
VCC/VIN +3.3VDC
GND GND
SCL GPIOy
SDA GPIOx
INT/IRQ not used

Tasmota Settings~

In the Configuration -> Configure Module page assign:

  1. GPIOx to I2C SDA
  2. GPIOy to I2C SCL

After configuring the GPIO's the driver will detect the APDS-9960 automatically. On first boot sensor will start in gesture mode. It will not appear in the webUI but it can be observed via MQTT messages in console:

21:34:21 MQT: tele/tasmota/RESULT = {"Gesture":"Off"}
+21:34:23 MQT: tele/tasmota/RESULT = {"Gesture":"On"}
+21:34:25 MQT: tele/tasmota/SENSOR = {"Time":"2019-10-31T21:34:25","APDS9960":{"None":1}}
+21:34:26 MQT: tele/tasmota/SENSOR = {"Time":"2019-10-31T21:34:26","APDS9960":{"Right":1}}
+21:34:29 MQT: tele/tasmota/SENSOR = {"Time":"2019-10-31T21:34:29","APDS9960":{"Down":1}}
+21:34:29 MQT: tele/tasmota/SENSOR = {"Time":"2019-10-31T21:34:29","APDS9960":{"Right":1}}
+21:34:31 MQT: tele/tasmota/SENSOR = {"Time":"2019-10-31T21:34:31","APDS9960":{"Left":1}}
+21:34:33 MQT: tele/tasmota/SENSOR = {"Time":"2019-10-31T21:34:33","APDS9960":{"Up":1}}
+21:34:35 MQT: tele/tasmota/SENSOR = {"Time":"2019-10-31T21:34:35","APDS9960":{"Down":1}}
+

When you enable RGBC mode with Sensor27 0 sensor will show up in web UI:

and in MQTT topic (according to TelePeriod):
MQT: tele/tasmota/SENSOR = {"Time":"2019-10-31T21:48:51","APDS9960":{"Red":282,"Green":252,"Blue":196,"Ambient":169,"CCT":4217,"Proximity":9}}

Commands~

Command Value Description
Sensor27 Show APDS9960 gesture/RGBC mode
Sensor27 0 / off Disable APDS9960 gesture mode/Enable RGBC mode
Sensor27 1 / on Enable APDS9960 gesture mode/Disable RGBC mode
Sensor27 2 / on Enable APDS9960 gesture mode/Disable RGBC mode with half gain
Sensor27 3 ...255 Set ATIME register for different integration times

Example Rules~

Device will be in RGBC mode until something is close to it, then it switches into gesture mode for 60 seconds.

Rule on APDS9960#Proximity=250 do backlog Sensor27 1; RuleTimer1 60 endon on Rules#Timer=1 do Sensor27 0 endon
+

Relay ON when ambient light is below 100 lux.

Rule on APDS9960#Ambient<100 do POWER ON endon
+

Control ON/OFF, brightness and color temperature with gestures

Rule on APDS9960#Long=1 do power toggle endon on APDS9960#Up=1 do dimmer + endon on APDS9960#Down=1 do dimmer - endon on APDS9960#Left=1 do ct + endon on APDS9960#Right=1 do ct - endon
+

Known Issues~

  1. The different PCB’s on the market seem to differ quite substantially regarding to their electrical characteristics. We have at least one case report, where this led to a malfunction on an ESP8266-board within Tasmota but in another library too. The exact technical reason can only be suspected, but it is probably related to electrical noise and/or power consumption.
    In the case from above the sensor measured an incorrect high proximity value, which resulted in repeated triggering of a "LONG" gesture. The solution was to decrease the gain factor for proximity and gesture. Therefore the argument 2 (sensor27 2) was introduced to change this at runtime.
    If you experience gesture sensing problems you could try this out, but if you measure proximity values <25 with nothing in front of the sensor (e.g. web interface after sensor27 0), then there is very likely another problem. It can be assumed, that the gesture sensitivity will suffer with reduced gain, so first try option 1 (=default).
    Beside that better wiring and maybe an additional capacitor over VCC and GROUND might be helpful.

  2. The measurement of the light level is briefly described in the datasheet and the open-source-libraries use the ambient-light-value directly from the sensor or calculate a LUX-value from RGB. Both variants are usable and differentiate between low and strong light, but the absolute values are questionable and at the moment we have an uncalibrated sensor.
    All known solutions use a fixed integration time, which is more or less the same as a fixed exposure time in photography. In contrast the TSL2561-library uses various integration times and maybe this is possible on the APDS9960 too.
    To eventually achieve this in the future, the option to set this integration time at runtime was added. Every argument between 3 and 255 sets the ATIME-register.
    The formula is: integration time = (256-ATIME)2,78 ms, so with the default value of 219 we get (256-219)2,78 = 102,86 ms. That means a smaller ATIME makes the integration time longer and more photons are captured, which might be usable for (very) low light conditions, because otherwise the sensor will saturate too early. The opposite is valid for a bigger ATIME value.
    The change of this value only makes sense for: users who need to change the sensitivity, if the sensor resides behind dark glass or want to contribute to the development of a new LUX-calculation in the driver. If we get enough feedback, this could lead to an improvement on the software side. Feel free to open (or search for) an issue, if you have measured the APDS9960 against other devices with different ATIME-values at different light levels. This is not a trivial task though.

Breakout Boards~

APDS-9960 GY-9960-LLC Adafruit APDS9960 Proximity, Light, RGB, and Gesture Sensor

Where to get~

APDS-9960 Datasheet

\ No newline at end of file diff --git a/API/index.html b/API/index.html new file mode 100644 index 0000000000..fc3ee0dc73 --- /dev/null +++ b/API/index.html @@ -0,0 +1,63 @@ + Basic API information - Tasmota
Skip to content

Basic API information~

Tasmota can easily be extended by developers using provided function pointers as callback Ids. This document lists the available callback function Ids. Read Sensor API for more information. ½ Callback availability can be checked by searching for either XdrvCall, XsnsCall, XdspCall, XnrgCall and XlgtCall.

Driver, Sensor, Energy and Light Callback Ids~

The following table lists Callback Ids and their availability for a Driver, Sensor or Energy service.

Callback Id Bool xdrv xsns xnrg xlgt Description
FUNC_SETTINGS_OVERRIDE x Override start-up settings
FUNC_PIN_STATE x 1 2 At GPIO configuration
FUNC_MODULE_INIT x 1 2 Init module specific parameters
FUNC_PRE_INIT 1 2 Once GPIO have been established
FUNC_INIT 1 3 2 At end of initialisation
FUNC_LOOP 1 2 In main loop
FUNC_EVERY_50_MSECOND 1 2
FUNC_EVERY_100_MSECOND 1 2
FUNC_EVERY_200_MSECOND x
FUNC_EVERY_250_MSECOND 1 3 2
FUNC_EVERY_SECOND 1 2
FUNC_SAVE_AT_MIDNIGHT x At midnight
FUNC_SAVE_BEFORE_RESTART 2 1 Just before a planned restart
FUNC_AFTER_TELEPERIOD 2 1 At end of teleperiod
FUNC_JSON_APPEND 2 1 3 Extend teleperiod JSON text
FUNC_WEB_SENSOR 2 1 3 Add sensor data to web GUI
FUNC_COMMAND x 1 2 3 4 When a command is not recognized
FUNC_COMMAND_DRIVER x x When command Driver<id> is executed
FUNC_COMMAND_SENSOR x x When command Sensor<id> is executed
FUNC_MQTT_SUBSCRIBE x At end of MQTT subscriptions
FUNC_MQTT_INIT x Once at end of MQTT connection
FUNC_MQTT_DATA x x Before decoding command
FUNC_SET_POWER x Before setting relays
FUNC_SET_DEVICE_POWER x x Set relay
FUNC_SHOW_SENSOR x When FUNC_JSON_APPEND completes
FUNC_ANY_KEY x
FUNC_ENERGY_EVERY_SECOND x
FUNC_ENERGY_RESET x
FUNC_RULES_PROCESS x x Process specific rule
FUNC_SERIAL x 1 2 3 Process serial data
FUNC_FREE_MEM x Show free memory for debugging
FUNC_BUTTON_PRESSED x x When a button is pressed
FUNC_WEB_ADD_BUTTON 1 2 Add a Configuration Button to GUI
FUNC_WEB_ADD_MAIN_BUTTON 1 2 Add a main button to GUI
FUNC_WEB_ADD_HANDLER 1 2 Add a webserver handler
FUNC_SET_CHANNELS 2 1
FUNC_SET_SCHEME x

The numbers represent the sequence of execution

Display Call back Ids~

The following table lists all Callback Ids for a Display service.

Callback Id Bool Description
FUNC_DISPLAY_INIT_DRIVER
FUNC_DISPLAY_INIT
FUNC_DISPLAY_EVERY_50_MSECOND
FUNC_DISPLAY_EVERY_SECOND
FUNC_DISPLAY_MODEL x
FUNC_DISPLAY_MODE
FUNC_DISPLAY_POWER
FUNC_DISPLAY_CLEAR
FUNC_DISPLAY_DRAW_FRAME
FUNC_DISPLAY_DRAW_HLINE
FUNC_DISPLAY_DRAW_VLINE
FUNC_DISPLAY_DRAW_LINE
FUNC_DISPLAY_DRAW_CIRCLE
FUNC_DISPLAY_FILL_CIRCLE
FUNC_DISPLAY_DRAW_RECTANGLE
FUNC_DISPLAY_FILL_RECTANGLE
FUNC_DISPLAY_TEXT_SIZE
FUNC_DISPLAY_FONT_SIZE
FUNC_DISPLAY_ROTATION
FUNC_DISPLAY_DRAW_STRING
FUNC_DISPLAY_ONOFF
FUNC_DISPLAY_NUMBER *
FUNC_DISPLAY_FLOAT *
FUNC_DISPLAY_NUMBERNC *
FUNC_DISPLAY_FLOATNC *
FUNC_DISPLAY_BRIGHTNESS *
FUNC_DISPLAY_RAW *
FUNC_DISPLAY_LEVEL *
FUNC_DISPLAY_SEVENSEG_TEXT *
FUNC_DISPLAY_SEVENSEG_TEXTNC *
FUNC_DISPLAY_SCROLLDELAY *
FUNC_DISPLAY_CLOCK *
FUNC_DISPLAY_SCROLLTEXT *

* TM1637, TM1638, MAX7219 and TM1650 driver only

Init sequence~

The following list shows a typical callback init sequence

CFG: Loaded from flash at FB, Count 1581
+xdrv - FUNC_SETTINGS_OVERRIDE
+xdrv - FUNC_PIN_STATE
+xsns - FUNC_PIN_STATE
+xdrv - FUNC_MODULE_INIT
+xlgt - FUNC_MODULE_INIT
+xdrv - FUNC_PRE_INIT
+xnrg - FUNC_PRE_INIT
+SRC: Restart
+xdrv - FUNC_SET_POWER
+xlgt - FUNC_SET_CHANNELS
+xdrv - FUNC_SET_DEVICE_POWER
+Project tasmota Wemos 2 Version 7.0.0.3(tasmota)-STAGE
+xdrv - FUNC_INIT
+xsns - FUNC_INIT
+I2C: ADS1115 found at 0x48
+xdrv - FUNC_LOOP
+xsns - FUNC_LOOP
+xdrv - FUNC_EVERY_50_MSECOND
+xlgt - FUNC_SET_CHANNELS
+xsns - FUNC_EVERY_50_MSECOND
+xdrv - FUNC_EVERY_100_MSECOND
+xsns - FUNC_EVERY_100_MSECOND
+xdrv - FUNC_EVERY_250_MSECOND
+xsns - FUNC_EVERY_250_MSECOND
+xdrv - FUNC_EVERY_SECOND
+xsns - FUNC_EVERY_SECOND
+WIF: Attempting connection...
+WIF: Network (re)scan started...
+WIF: Attempting connection...
+WIF: Attempting connection...
+WIF: Attempting connection...
+WIF: Network 0, AP1, SSId indebuurt1, Channel 1, BSSId 24:D3:F2:97:C0:A1, RSSI -86, Encryption 1
+WIF: Network 1, AP2, SSId indebuurt2, Channel 5, BSSId A0:AB:1B:7D:42:AC, RSSI -42, Encryption 1
+WIF: Network 2, AP-, SSId indebuurt3, Channel 12, BSSId 60:E3:27:58:77:E6, RSSI -84, Encryption 1
+WIF: Connecting to AP2 indebuurt2 in mode 11N as wemos2...
+WIF: Attempting connection...
+WIF: Attempting connection...
+WIF: Attempting connection...
+WIF: Connected
+xdrv - FUNC_WEB_ADD_HANDLER
+xsns - FUNC_WEB_ADD_HANDLER
+HTP: Web server active on wemos2 with IP address 192.168.2.191
+NTP: Drift 0, (UTC) Wed Nov 06 13:57:08 2019, (DST) Sun Mar 31 02:00:00 2019, (STD) Sun Oct 27 03:00:00 2019
+APP: Boot Count 500
+MQT: Attempting connection...
+MQT: Connected
+MQT: tele/wemos2/LWT = Online (retained)
+MQT: cmnd/wemos2/POWER =
+MQT: Subscribe to cmnd/wemos2/#
+MQT: Subscribe to cmnd/sonoffs/#
+MQT: Subscribe to cmnd/DVES_15568C_fb/#
+xdrv - FUNC_MQTT_SUBSCRIBE
+MQT: tele/wemos2/INFO1 = {"Module":"Generic","Version":"7.0.0.3(tasmota)","FallbackTopic":"cmnd/DVES_15568C_fb/","GroupTopic":"cmnd/sonoffs/"}
+MQT: tele/wemos2/INFO2 = {"WebServerMode":"Admin","Hostname":"wemos2","IPAddress":"192.168.2.191"}
+MQT: tele/wemos2/INFO3 = {"RestartReason":"Software/System restart"}
+MQT: stat/wemos2/RESULT = {"POWER1":"OFF"}
+MQT: stat/wemos2/POWER1 = OFF
+MQT: stat/wemos2/RESULT = {"POWER2":"ON"}
+MQT: stat/wemos2/POWER2 = ON
+xdrv - FUNC_MQTT_INIT
+CFG: Saved to flash at FA, Count 1582, Bytes 4096
+
\ No newline at end of file diff --git a/AS3935/index.html b/AS3935/index.html new file mode 100644 index 0000000000..56e52af67d --- /dev/null +++ b/AS3935/index.html @@ -0,0 +1,5 @@ + AS3935 Franklin Lightning sensor - Tasmota
Skip to content

AS3935 Franklin Lightning sensor~

This feature is included only in tasmota-sensors and tasmota32 binaries

When compiling your build add the following to user_config_override.h:

#ifndef USE_AS3935 
+#define USE_AS3935      // [I2cDriver48] Enable AS3935 Franklin Lightning Sensor (I2C address 0x03) (+5k4 code)
+#endif
+

The AS3935 is a programmable fully integrated Lightning Sensor IC that detects the presence and approach of potentially hazardous lightning activity in the vicinity and provides an estimation on the distance to the head of the storm. The embedded lightning algorithm checks the incoming signal pattern to reject the potential man-made disturbers.

Tasmota driver includes:

  • support for all AS3935 Parameters
  • support of all related commands for configuration
  • support of NF-Auto Function to auto Threshold the Noise Reduction
  • support of Auto Disturber
  • support of calibrating the internal OSC with switchable Caps

Configuration~

Wiring~

AS3935 ESP
GND GND
VCC 3.3V
SDA GPIOx
SCL GPIOy
IRQ GPIOz

Warning

"Sensor uses a software interrupt. Make sure that the connection with the IRQ pin is stable to prevent flicker."

Tasmota Settings~

In the Configuration -> Configure Module page assign:

  1. GPIOx to I2C SDA
  2. GPIOy to I2C SCL
  3. GPIOz to AS3935

Note for I2C Use

You will need to wire up all of the associated pins for the different boards.
- Always connect CS and MISO to GND.
- If "SI" is present, you need to connect to VCC.
See some wiring on the Board pictures below. I2C wiring

Module Settings

After a reboot the driver will detect AS3935 automatically.

webUI readout

and in MQTT topic when an Event occurs:

{"Time":"2020-01-01T17:07:07","AS3935":{"Event":4,"Distance":12,"Energy":58622,"Stage":1}}
+

Commands and Use~

Console Commands Description values Bitlength
AS3935power Power On/Off the Device 0/1 (On/Off) (1 Bit)
AS3935setnf Noise Floor Level value from 0-7 (3 Bit)
AS3935setml Minimum number of lightning 1, 5, 9, 16 (2 bit)
AS3935default load default for Sensor and Settings no argument
AS3935setgain Set Indoor/Outdoor Indoors/Outdoors (Ascii)
AS3935settunecaps Internal Tuning Cap. value from 0-15 (4 Bits)
AS3935setrej Spike rejection value from 0-15 (4 Bits)
AS3935setwdth Watchdog threshold value from 0-15 (4 Bits)
AS3935setminstage min stage that could be come with NFautotune value from 0-15: 0-7 Indoors, 8-15 Outdoors (4 Bits)
AS3935disturber Set Disturber 0/1 (On/Off) (1 Bit)
AS3935autonf Set Auto Tune for Noise Level 0/1 (On/Off) (1 Bit)
AS3935autodisturber Set Auto-Disturber 0/1 (On/Off) (1 Bit)
AS3935autonfmax Auto Tune with INDOOR and OUTDOOR 0/1 (On/Off) (1 Bit)
AS3935lightevent mqtt messages only for lightning events 0/1 (On/Off) (1 Bit)
AS3935noirqevent suppress mqtt "IRQ with no Event" Messages 0/1 (On/Off) (1 Bit)
AS3935settings show all settings no argument
AS3935calibrate auto calibrate the internal Capacitors no argument
AS3935disttime time for reset Disturber in auto-mode 0-15 min. (4 Bit)
AS3935nftime time for auto-Nf threshold 0-15 min (4 Bit)

Note for AS3935Calibrate

Normally you don't need the calibrate function. If you buy the AS3935, the module has a sticker on it with the calibrated cap. Use AS3935Settunecaps for setting up this value.
if the calibration fails, it is possible that you have a board with the wrong caps on it! (some CJMCU-Boards)

Mqtt Events:~

No. Description Suppress with
Command
AS3935lightevent
Suppress with
Command
AS3935noirqevent
0 no event
1 Lightning with Distance detected
2 Lightning out of Distance
3 Distance cannot be determined
4 Storm is Overhead
5 Noise level too high *
6 Disturber detected *
7 Irq with no Event detected *
8 Irq Pin flicker detected
9 Device is Powerd Off

NF setting table:~

Setting table of the NF-noise sensitivity and stages:

Stages NF-LEV AFE-GB uVrms Sensitivity
Stage 0 000 Indoors 28 highly sensitive
Stage 1 001 Indoors 45
Stage 2 010 Indoors 62
Stage 3 011 Indoors 78
Stage 4 100 Indoors 95
Stage 5 101 Indoors 112
Stage 6 110 Indoors 130
Stage 7 111 Indoors 146
Stage 8 000 Outdoors 390
Stage 9 001 Outdoors 630
Stage 10 010 Outdoors 860
Stage 11 011 Outdoors 1100
Stage 12 100 Outdoors 1330
Stage 13 101 Outdoors 1570
Stage 14 110 Outdoors 1800
Stage 15 111 Outdoors 2000 less sensitive

Hint and Tips

  • take a stabilized power supply.
  • keep a distance of at least 200 mm from the ESP and the AS3395
  • be careful with "ready to Use" devices like Sonoff, etc. they don't have a good power supply inside.
  • use a Ferrit clamp filter on the sensor cable.
  • wire all related Pins for I2C use.

You can find some troubleshooting tips in the Tasmota GitHub discussion.

Breakout Boards and Wiring for I2C use:~

PlayingwithFusion Board:
~

AS3935

Sparkfun Board V2.0:
~

AS3935

Embedded Adventures Board:
~

AS3935

CJMCU Board:
~

AS3935

GY-AS3935 Board:
~

AS3935


Breakout Boards Issues:~

Warning

"Some type of this sensor have wrong capacitors (100pF and 1000pF) installed and the calibration fails. The correct caps are 680pF and 270pF."

AS3935

Datasheet:~

Datasheet from Mouser Web Site

\ No newline at end of file diff --git a/AWS-IoT-cert/index.html b/AWS-IoT-cert/index.html new file mode 100644 index 0000000000..99fd0bc626 --- /dev/null +++ b/AWS-IoT-cert/index.html @@ -0,0 +1,83 @@ + AWS IoT cert - Tasmota
Skip to content

AWS IoT cert

This page contains deprecated information.

We advise to use the AWS IoT password based authentication.

New simplified and automated configuration

We now provide easy to use AWS CloudFormation templates to generate the private key and sign the certificate. The manual method is now in Appendix

This feature is not included in precompiled binaries

To use it you must compile your build.

Add the following to user_config_override.h:

#ifndef USE_MQTT_TLS
+#define USE_MQTT_TLS
+#define USE_MQTT_TLS_CA_CERT // Optional but highly recommended
+#endif
+#ifndef USE_MQTT_AWS_IOT
+#define USE_MQTT_AWS_IOT
+#endif
+#ifdef USE_DISCOVERY
+#undef USE_DISCOVERY
+#endif
+

As of Tasmota version 6.6.0.3, the device-specific credentials are no longer restricted to being set at compile time only. You can now use the same firmware for all your devices. AWS IoT credentials can be set through the Console at runtime and are stored in flash memory. Credentials will survive firmware updates and OTA. Credentials will not survive a full Flash reset Reset 5 or Reset 6 nor will it survive a System Parameter Flash reset Reset 3

Benefits~

AWS IoT provides secure, bi-directional communication between Internet-connected devices such as sensors, actuators, embedded micro-controllers. This basically allows to communicate in both direction from the cloud using MQTT over secure channels using TLS.

1. More Alexa controls~

It's actually easy to develop smarthome Alexa skills, so that you can control your whole house. Currently you can only use the local Philips Hue/Wemo emulation - limited to lights and switches. You can imagine for instance controlling your Sonoff RF Bridge and send IR codes to your TV.

Alexa skills need to communicate back to your devices, which is easy using MQTT and AWS IoT

2. No need for a local gateway~

Of course you can do it with a local gateway like Raspberry PI using many of the open-source solutions (Domoticz...).

You can also do it entirely from the cloud without the hassle of managing and updating a local gateway.

On top of it, AWS IoT provides tools to collect and archive your data, automate (AWS IoT things).

Maximum security~

Keep in mind that AWS IoT is based with 'security first' in mind. All the data in AWS IoT is your data and is not shared with anyone else.

Communication is done over TLS 1.2 tunnels, using client certificates to authenticate each device. Up to now it was challenging to enable TLS on ESP8266 because of the high memory requirements of TLS.

Thanks to the switch of Arduino to BearSSL and aggressive optimization, the amount of memory needed is as low as 6.0k during normal operation, and an additional 6.6k during connection (TLS handshake). This makes it totally doable with standard 'Tasmota' firmware with Web and Hue emulation activated. You should see more than 20k of memory still available.

Caveats~

AWS IoT requires each Tasmota device to have its own distinct Private Key and Certificate (~800 bytes). Although you could imagine to use the same Private Key in all your devices, this is considered as a very bad practice. You are warned!

During TLS handshake, a secondary stack of 5.3k is allocated on the heap to allow BearSSL to have enough stack room. Memory is freed at the end of the handshake. Allocating such big chunks of memory can cause issues when heap fragmentation gets too high. If you see memory going below 15KB, you may experience crashes.

Cost~

AWS provides a Free Tier that allows you to use some services for free up to a specific level. For example, it allows you to have 50 devices connecting 24 hours a day exchanging 300 messages per day. For a typical house, there is a good chance the service costs you nothing (the first year).

How to configure?~

AWS IoT requires a distinct Private Key and Certificate per Tasmota device. Currently you need to custom compile your Tasmota firmware and burn the Key and Certificate in your firmware. We will later explore how to configure them separately.

Here is a simple guide.

0. Open an AWS Account~

If you don't have already one, just follow the guide: https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/

1. Prerequisites~

You will need to install/compile the following:

  • Complete environment to compile Tasmota, ex: PlatformIO (PlatformIO)
  • Recent version of openssl

2. Enable AWS IoT in Tasmota~

Using your favorite IDE, create user_config_override.h and add the required compilation directives as documented at the top of this article.

Note: TLS handshake takes ~1.2s on ESP8266 @80MHz. You may choose to switch to 160MHz if the power supply of your device supports it. If you do so, handshake time should be ~0.7s.

Compile the firmware and ensure it completes successfully.

This step is only to check compilation goes well. Your firmware is still not usable since it does not contain the Private Key + Certificate.

3. Flash your device~

Flash your device the normal way; either through serial or OTA. If you use OTA, first flash a sonoff-minimal firmware, then your target firmware.

4. Configure AWS IoT Policy (to be done once)~

Open the AWS Console and select the target region. In the example below we will use (EU) Frankfurt (eu-central-1).

Download the CloudFormation template Tasmota-MqttPolicy and use it in AWS CloudFormation.

Or click on the link for the region you have chosen:

Region Code Launch
US East (N. Virginia) us-east-1 Launch IotThingGenerator in us-east-1
EU (Frankfurt) eu-central-1 Launch IotThingGenerator in eu-west-1
EU (Paris) eu-west-3 Launch IotThingGenerator in eu-west-1
  1. At the Create Stack screen, click Next. MqttPolicy01

  2. At the Specify stack details screen, keep all default parameters and click Next. MqttPolicy02

  3. At the Configure stack options screen, keep all default parameters and click Next. MqttPolicy03

  4. At the Review TasmotaMqttPolicy screen, scroll down and click Create Stack. MqttPolicy04

  5. The stack usually takes less than 2 minutes to complete. Wait for it to reach CREATE_COMPLETE state. MqttPolicy05

  6. If you have left the parameter RetentionPolicy to Retain, then you can delete this CloudFormation stack (it will not delete the Policy). Click on the Delete button. MqttPolicy06

  7. After less than 2 minutes, the stack should have reached the state DELETE_COMPLETE MqttPolicy07

5. Create an AWS IoT Thing with Private Key and Certificate (once per Tasmota device)~

The provided AWS CloudFormation template will create the required resources to create:

  • One AWS IoT Thing
  • One Private key
  • One Certificate signed by AWS IoT
  • Temporary resources (AWS Lambda functions, AWS IAM resources) that you can delete once the Tasmota thing is created.

Open the AWS Console and select the target region. In the example below we will use (EU) Frankfurt (eu-central-1).

Download the CloudFormation template Tasmota-Thing and use it in AWS CloudFormation.

Or click on the link for the region you have chosen:

Region Code Launch
US East (N. Virginia) us-east-1 Launch IotThingGenerator in us-east-1
EU (Frankfurt) eu-central-1 Launch IotThingGenerator in eu-west-1
EU (Paris) eu-west-3 Launch IotThingGenerator in eu-west-1
  1. At the Create Stack screen, click Next. MqttThing01

  2. At the Specify stack details screen, keep all default parameters and click Next. Note: you can change the name of the Thing in AWS IoT by specifying the parameter ThingParamName. MqttThing02

  3. At the Configure stack options screen, keep all default parameters and click Next. MqttPolicy03

  4. At the Review Tasmota-91 screen, scroll down, check the box I acknowledge that AWS CloudFormation might create IAM resources. and click Create Stack. MqttThing03

  5. The stack usually takes less than 4 minutes to complete. Wait for it to reach CREATE_COMPLETE state. MqttThing04

    You need to copy & paste the contents of the Outputs tab of the CloudFormation stack: MqttHost, TlsKey1, TlsKey2 MqttThing05 MqttThing06 MqttThing07 Keep a copy of those parameters in a file, you might need them again.

Cleaning: to avoid having CloudFormation templates piling up in your console, you can delete them. The created resources will remain, if you have left the parameter RetentionPolicy to Retain.

6. Configure Tasmota device~

This is the last step, you need to configure the MQTT parameters. The easiest way is through the web console. We will only cut and paste parameters from the Outputs tab of the CloudFormation console.

Configure the AWS EndPoint~

Copy and paste in the web console the content of MqttHost

Example:

Backlog MqttHost <your_endpoint>-ats.iot.eu-central-1.amazonaws.com; MqttPort 8883
+

This will trigger a reboot of the device.

Optional, change the topic to distinguish the devices from each others: Topic sonoff/Tasmota-01

Check that the key store is empty~

Type the following command: TLSKey

hh:mm:ss CMD: TLSKey
+hh:mm:ss MQT: stat/<topic>/RESULT = {"TLSKey1":-1,"TLSKey2":-1}
+

If both values are -1, it means it does not contain any key.

If you need to reset the key store, use the command TLSKey 0.

Configure the Private Key and Certificate~

AWS IoT credentials are composed of two distinct parts, first a Private Key - this is the secret that will allow your device to prove it is who it pretends to be. Consider this as sensitive as a password. The Private Key is exactly 32 bytes (256 bits).

The second part is the Certificate delivered by AWS IoT. Tasmota will also need it to authenticate to the AWS IoT endpoint.

Both credentials must be stored in Tasmota Flash memory, in that order, using the new TLSKey command. TlsKey1 stores the Private Key. TlsKey2 stores the Certificate. There is no command to retrieve the private key from a Tasmota device, but keep in mind this secret information can easily be dumped via Serial if somebody gets physical access to the device (ESP8266 does not contain any secure storage area).

Simply Copy and Paste the two commands from TlsKey1 and TlsKey2.

TLSKey1 <secret_key_secret_key_secret_key>=
+TLSKey2 MIIC<certificate_very_long_string>=
+

TLSKey1 and TLSKey2 must be entered in that order. If successful, you should see a message similar to:

hh:mm:ss MQT: stat/<topic>/RESULT = {"TLSKey1":32,"TLSKey2":641}
+

You need to check that both values are not "-1". The value for "TLSKey1" should always be 32. The value for "TLSKey2" varies depending on several parameters, and should be within the 640-700 bytes range.

Connect to AWS IoT~

Once the TLSKey1 and TLSKey2 are entered, Tasmota will try to connect to AWS IoT.

Keep in mind that AWS IoT does not support 'retained' messages. Whatever the 'retained' configuration in Tasmota, messages are always published as 'retained=false'.

Here is an example of output you should see:

00:00:04 HTP: Web server active on sonoff-4585 with IP address 192.168.1.59
+00:00:04 UPP: Multicast (re)joined
+21:28:25 MQT: Attempting connection...
+21:28:25 MQT: AWS IoT endpoint: xxxxxxxxxxxxx-ats.iot.eu-central-1.amazonaws.com
+21:28:26 MQT: AWS IoT connected in 1279 ms
+21:28:26 MQT: Connected
+21:28:26 MQT: tele/tasmota/LWT = Online
+21:28:26 MQT: cmnd/tasmota/POWER =
+21:28:26 MQT: tele/tasmota/INFO1 = {"Module":"Sonoff Basic","Version":"6.5.0.14(sonoff)","FallbackTopic":
+"cmnd/DVES_67B1E9_fb/","GroupTopic":"sonoffs"}
+

7. Check end-to-end communication~

In the AWS IoT console, click on "Test" in the left panel.

In the "Subscription topic" field, type +/sonoff/# then click on "Subscribe to topic". This will display all MQTT messages received. Type a command in the Web Tasmota console, you should see MQTT message flow.

Enjoy!

8. Cleaning~

Cleaning: to avoid having CloudFormation templates piling up in your console, you can delete them. The created resources will remain, if you have left the parameter RetentionPolicy to Retain.

9. Troubleshooting~

TLSError shows any error at the TLS level. See here for most common error codes.

For implementation details, see here~

Appendix: Manual configuration~

1. Prerequisites~

You will need to install/compile the following:

  • Complete environment to compile Tasmota, ex: PlatformIO (PlatformIO)
  • Recent version of openssl

2. Configure AWS IoT (to be done once)~

Open the AWS Console.

Click on "Services" and select "IoT Core".

Select the AWS Region where you want to locate your data, for ex: "(EU) Frankfurt".

AWS_IoT

Now we need to create a security policy to allow your Tasmota devices to connect to AWS IoT, publish and subscribe to topics.

On the left panel, click on "Secure" > "Policies". Click on the "Create" button in the upper right corner.

Enter in the "Name" field, enter the name of your policy, for ex: "TasmotaMqttPolicy".

Create_policy

Then click on "Advanced mode" Cut and paste the policy below. Click on "Create" in the lower right corner.

{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Effect": "Allow",
+      "Action": [
+        "iot:Connect",
+        "iot:Publish",
+        "iot:Subscribe",
+        "iot:Receive"
+      ],
+      "Resource": "*"
+    }
+  ]
+}
+

3. Create a Private Key and Certificate (once per Tasmota device)~

Do not use the default AWS IoT feature to generate your private key online. It creates a 2048 bits RSA key. Instead we are using elliptic curves keys - they are much smaller in memory than RSA keys (this saves ~1k of memory) and the handshake is significantly faster.

First create an ECC private key for your device (as described in this Blog). Keep you private key in a safe place.

$ openssl ecparam -name prime256v1 -genkey -out tasmota-01.key
+

Next, using this private key, create a certificate signing request (CSR). When asked enter the certificate details. This is not really used later, you can just enter a 2 letters country code like "EU" and leave all other fields blank (type 8 times enter).

$ openssl req -new -sha256 -key tasmota-01.key -nodes -out tasmota-01.csr
+You are about to be asked to enter information that will be incorporated
+into your certificate request.
+What you are about to enter is what is called a Distinguished Name or a DN.
+There are quite a few fields but you can leave some blank
+For some fields there will be a default value,
+If you enter '.', the field will be left blank.
+-----
+Country Name (2 letter code) []:EU <enter>
+State or Province Name (full name) []: <enter>
+Locality Name (eg, city) []: <enter>
+Organization Name (eg, company) []: <enter>
+Organizational Unit Name (eg, section) []: <enter>
+Common Name (eg, fully qualified host name) []: <enter>
+Email Address []: <enter>
+
+Please enter the following 'extra' attributes
+to be sent with your certificate request
+A challenge password []: <enter>
+

Next ask AWS IoT to sign your key with its certificate. On the left panel, click on "Secure" > "Certificates". Click on "Create a certificate".

Then choose the "Create with CSR" button, locate your CSR file from above. Click on "Upload file". Download the certificate file, click on "Download", save the file as "tasmota-01.cert.pem".

Important: don't forget to click on the "Activate" to activate the certificate.

Your Private Key and Certificates are ready to use. Now we need to register the Tasmota Device.

4. Write down your AWS IoT endpoint (same for all devices)~

Click on "Settings" in the left panel. You should see a field called "Custom endpoint". Write down the endpoint domain name. It should look like this (if you have chosen the Frankfurt region:

<xxxxxxxxxxxxxx>-ats.iot.eu-central-1.amazonaws.com
+

This is your MQTT endpoint, the port is 8883 - MQTT over TLS.

5. Register the device in AWS IoT (once per Tasmota device)~

Now on the left pane, click on "Manage" > "Things". Click on "Register a thing", then "Create a single thing".

Give your device a name like "Tasmota-01". Scroll down and click "Next" at the bottom right. Then click on "Create thing without a certificate".

Now we need to associate the certificate created earlier to your device. In the left panel, click back on "Secure" > "Certificates". Select the certificate created earlier. In the next pane, click on "Actions" in the upper right part. First select "Attach policy", check "TasmotaMqttPolicy" and "Attach". Click again on "Actions" and select "Attach thing", check "Tasmota-01" and "Attach".

Your setup is done in AWS IoT. Let's proceed to the custom firmware.

Tasmota-01

8. Prepare your AWS IoT credentials~

You will now need to convert your AWS IoT credentials to Tasmota commands. Credentials are composed of two distinct parts, first a Private Key - this is the secret that will allow your device to prove it is who it pretends to be. Consider this as sensitive as a password. The Private Key is exactly 32 bytes (256 bits).

The second part is the Certificate delivered by AWS IoT. Tasmota will also need it to authenticate to the AWS IoT endpoint.

Both credentials must be stored in Tasmota Flash memory, in that order, using the new TLSKey command.

a. First check that the key store is empty~

Type the following command: TLSKey

hh:mm:ss CMD: TLSKey
+hh:mm:ss MQT: stat/tasmota/RESULT = {"TLSKey1":-1,"TLSKey2":-1}
+

If both values are -1, it means it does not contain any key.

If you need to reset the key store, use the command TLSKey 0.

b. Convert the Private Key~

We will extract the 32 bytes Private key from tasmota-01.key generated above.

1-line method, use the following command: (fake key below)

openssl ec -in tasmota-01.key -inform PEM -outform DER | openssl asn1parse -inform DER | head -3 | tail -1 | awk -F':' '{ print $4 }' | xxd -r -p | base64 | echo "TLSKey1 $(</dev/stdin)"

Example:

openssl ec -in tasmota-01.key -inform PEM -outform DER | openssl asn1parse -inform DER | head -3 | tail -1 | awk -F':' '{ print $4 }' | xxd -r -p | base64 | echo "TLSKey1 $(</dev/stdin)"
+read EC key
+writing EC key
+TLSKey1 UvBgyCuPr/lKSgwumf/8o/mIsKQPBHn3ZZAGZl4ui9E=
+

Cut and paste the command starting with TLSKey1 ... into the Tasmota Web Console or through MQTT.

hh:mm:ss CMD: TLSKey1 UvBgyCuPr/lKSgwumf/8o/mIsKQPBHn3ZZAGZl4ui9E=
+hh:mm:ss MQT: stat/tasmota/RESULT = {"TLSKey1":32,"TLSKey2":-1}
+

Alternative method:

Convert the Private Key file from PEM to DER (binary format):

openssl ec -in tasmota-01.key -inform PEM -outform DER -out tasmota-01.key.der

Dump the ASN.1 format from the DER file:

openssl asn1parse -inform DER -in tasmota-01.key.der
+    0:d=0  hl=2 l= 119 cons: SEQUENCE          
+    2:d=1  hl=2 l=   1 prim: INTEGER           :01
+    5:d=1  hl=2 l=  32 prim: OCTET STRING      [HEX DUMP]:52F060C82B8FAFF94A4A0C2E99FFFCA3F988B0A40F0479F7659006665E2E8BD1
+   39:d=1  hl=2 l=  10 cons: cont [ 0 ]        
+   41:d=2  hl=2 l=   8 prim: OBJECT            :prime256v1
+   51:d=1  hl=2 l=  68 cons: cont [ 1 ]        
+   53:d=2  hl=2 l=  66 prim: BIT STRING        
+

Then convert the byte stream after [HEX DUMP] to base64 and use it with the TLSKey1 command.

c. Convert the Certificate~

Similarly you will need to convert the file "tasmota-01.cert.pem" generated above to a Tasmota command.

1-line version, use the following command:

openssl x509 -in tasmota-01.cert.pem -inform PEM -outform DER | base64 | echo "TLSKey2 $(</dev/stdin)"

Example: (fake certificate)

openssl x509 -in tasmota-01.cert.pem -inform PEM -outform DER | base64 | echo "TLSKey2 $(</dev/stdin)"
+TLSKey2 MIICfTCCAWWgAwIBAgIUMPd6KefJYqwIHxzgCk1kEXIjHhkwDQYJKoZIhvcNAQELBQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20gSW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTE5MDgwNDE5MjI1NVoXDTQ5MTIzMTIzNTk1OVowDTELMAkGA1UEBhMCRVUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASCzFZFhPXBbr6G6gbhT/rGXROhN5AHdwhxfpY55xi3c1W3TBuvUdZYdU2Urc9t4ces9Nz3UcF1xfMBvIpVqMVco2AwXjAfBgNVHSMEGDAWgBT3n7seZ1eHUIcZCcuwn0fkEad77TAdBgNVHQ4EFgQUUruspk3ohBJB8buA8qq9kQIZUs0wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBAFPKpVjaowYY3taAcKsSxfwkwzglI2eUlnmSdnu6WZkNEdiYpx8QVYb+miJnIyTVaE3bzkTr4PvObwf4Vs92uMtIQ5BuU1lj8EdfsZAs6uA1rqfQIl0n7ty3bErtVb3d+uUBm4C1b9mtbQS45itfrTvzWEoLuWflUxAFqyYVmNPNHTfPFLjAP9dcpWk+388pRl3jkGo1qiFrEp+ucQMtkqwn3lfVXlRFuGY6GxKg2lBWvqW5CuHaBhFjeT63pqUmjj76VrBk/Vp9BMjIJ3a1omuW3ZUGCPdXUVqj4/m2pXoYIGW7l/fZpfV4piKOW1tcxeX/iBdgPUL1XUMbCtBybbc=
+

Copy and paste the last line starting with TLSKey2 ... into the Web Tasmota Console or through MQTT. Note: you cannot use this command through Serial, it is bigger than the max serial buffer (520 bytes).

hh:mm:ss CMD: TLSKey2 MIICfTCCAWWgAwIBAgIUMPd6KefJYqwIHxzgCk1kEXIjHhkwDQYJKoZIhvcNAQELBQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20gSW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTE5MDgwNDE5MjI1NVoXDTQ5MTIzMTIzNTk1OVowDTELMAkGA1UEBhMCRVUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASCzFZFhPXBbr6G6gbhT/rGXROhN5AHdwhxfpY55xi3c1W3TBuvUdZYdU2Urc9t4ces9Nz3UcF1xfMBvIpVqMVco2AwXjAfBgNVHSMEGDAWgBT3n7seZ1eHUIcZCcuwn0fkEad77TAdBgNVHQ4EFgQUUruspk3ohBJB8buA8qq9kQIZUs0wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQ
+hh:mm:ss MQT: stat/tasmota/IR2/RESULT = {"TLSKey1":32,"TLSKey2":641}
+

You need to check that both values are not "-1". The value for "TLSKey1" should always be 32. The value for "TLSKey2" varies depending on several parameters, and should be within the 640-700 bytes range.

Alternative version:

Convert your certificate from PEM to DER (binary) format:

openssl x509 -in tasmota-01.cert.pem -inform PEM -outform DER -out tasmota-01.cert.der

Then convert the Certificate to plain base64 in a single line (use -A flag):

openssl base64 -e -in tasmota-01.cert.der -A -out tasmota-01.cert.b64

Then use the command TSLKey2 <base64> and replace <base64> with the content of tasmota-01.cert.b64.

\ No newline at end of file diff --git a/AWS-IoT/index.html b/AWS-IoT/index.html new file mode 100644 index 0000000000..8de3e257bb --- /dev/null +++ b/AWS-IoT/index.html @@ -0,0 +1,31 @@ + AWS IoT - Tasmota
Skip to content

AWS IoT

Tip

New simplified configuration, since AWS IoT doesn't require private certificates anymore and can use password authentication. Requires v8.5.0.1 or higher. The certificate based authentication is kept for reference.

New simplified and automated configuration

We now provide easy to use AWS CloudFormation templates to generate the private key and sign the certificate. The manual method is now in Appendix

This feature is not included in precompiled ESP8266 binaries, except tasmota-zbbridge. It is included in all ESP32 builds.

To use it you must compile your build.

Add the following to user_config_override.h:

#ifndef USE_MQTT_TLS
+#define USE_MQTT_TLS
+#define USE_MQTT_TLS_CA_CERT // Optional but highly recommended
+#endif
+#ifndef USE_MQTT_AWS_IOT_LIGHT
+#define USE_MQTT_AWS_IOT_LIGHT
+#endif
+#ifdef USE_DISCOVERY
+#undef USE_DISCOVERY
+#endif
+

As of Tasmota version 8.5.0.1, device-specific private key and certificate are no more required. We introduces AWS_IOT_LIGHT to use password based authentication. Legacy private certificate authentication can be found here.

Benefits~

AWS IoT provides secure, bi-directional communication between Internet-connected devices such as sensors, actuators, embedded micro-controllers. This basically allows to communicate in both direction from the cloud using MQTT over secure channels using TLS.

1. More Alexa controls~

It's actually easy to develop smarthome Alexa skills, so that you can control your whole house. Currently you can only use the local Philips Hue/Wemo emulation - limited to lights and switches. You can imagine for instance controlling your Sonoff RF Bridge and send IR codes to your TV.

Alexa skills need to communicate back to your devices, which is easy using MQTT and AWS IoT

2. No need for a local gateway~

Of course you can do it with a local gateway like Raspberry PI using many of the open-source solutions (Domoticz...).

You can also do it entirely from the cloud without the hassle of managing and updating a local gateway.

On top of it, AWS IoT provides tools to collect and archive your data, automate (AWS IoT things).

Maximum security~

Keep in mind that AWS IoT is based with 'security first' in mind. All the data in AWS IoT is your data and is not shared with anyone else.

Communication is done over TLS 1.2 tunnels. Thanks to the switch of Arduino to BearSSL and aggressive optimization, the amount of memory needed is as low as 6.0k during normal operation, and an additional 6.6k during connection (TLS handshake). This makes it totally doable with standard 'Tasmota' firmware with Web and Hue emulation activated. You should see more than 20k of memory still available.

Caveats~

During TLS handshake, a secondary stack of 4.8k is allocated on the heap to allow BearSSL to have enough stack room. Memory is freed at the end of the handshake. Allocating such big chunks of memory can cause issues when heap fragmentation gets too high. If you see memory going below 15KB, you may experience crashes.

Cost~

AWS provides a Free Tier that allows you to use some services for free up to a specific level. For example, it allows you to have 50 devices connecting 24 hours a day exchanging 300 messages per day. For a typical house, there is a good chance the service costs you nothing (the first year).

How to configure?~

AWS IoT now works with Password authentication. Although it is not a good practice to use the same password for all your devices, it is technically working and the easiest way to start with Tasmota and AWS IoT.

Here is a simple guide.

0. Open an AWS Account~

If you don't have already one, just follow the guide: https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/

1. Prerequisites~

You will need to install/compile the following:

  • Complete environment to compile Tasmota, ex: PlatformIO (PlatformIO)

2. Enable AWS IoT in Tasmota~

Add the required compilation directives to user_config_override.h as documented at the top of this article.

Note: TLS handshake takes ~1.2s on ESP8266 @80MHz. You may choose to switch to 160MHz if the power supply of your device supports it. If you do so, handshake time should be ~0.7s.

Compile the firmware and ensure it completes successfully.

This step is only to check compilation goes well. Your firmware is still not usable since it does not contain the Private Key + Certificate.

3. Flash your device~

Flash your device the normal way; either through serial or OTA.

4. Configure AWS IoT Policy (to be done once)~

Open the AWS Console and select the target region. In the example below we will use (EU) Frankfurt (eu-central-1).

Download the CloudFormation template TasmotaAuth and use it in AWS CloudFormation.

Or click on the link for the region you have chosen:

Region Code Launch
US East (N. Virginia) us-east-1 Launch TasmotaAuth in us-east-1
EU (Frankfurt) eu-central-1 Launch TasmotaAuth in eu-west-1
EU (Paris) eu-west-3 Launch TasmotaAuth in eu-west-1
  1. At the Create Stack screen, click Next. MqttAuth01

  2. At the Specify stack details screen, keep all default parameters and click Next. MqttAuth02

  3. At the Configure stack options screen, keep all default parameters and click Next. MqttAuth03

  4. At the Review TasmotaAuth screen, scroll down, click the checkbox to acknowledge the creation of IAM resources, and click Submit.

  5. The stack usually takes less than 1 minute to complete. Wait for it to reach CREATE_COMPLETE state.

  6. Copy the commands under BackLogCommand, you will need it to configure Tasmota devices. The BackLogCommand will be found on the Cloudformation console, under the Outputs tab.

  7. If you have left the parameter RetentionPolicy to Retain, then you can delete this CloudFormation stack (it will not delete the Policy). Click on the Delete button. MqttAuth07

  8. After less than 2 minutes, the stack should have reached the state DELETE_COMPLETE

6. Configure Tasmota device~

This is the last step, you need to configure the MQTT parameters. The easiest way is through the web console. We will only paste the backlog command from the Outputs tab of the CloudFormation console as previously instructed.

Example:

BackLog SetOption3 1; SetOption103 1; MqttHost a............u-ats.iot.eu-central-1.amazonaws.com; MqttPort 443; MqttUser tasmota?x-amz-customauthorizer-name=TasmotaAuth; MqttPassword YknLuSd2tBY2HodwI/7RqA==
+
+11:28:44 CMD: BackLog SetOption3 1; SetOption103 1; MqttHost a............u-ats.iot.eu-central-1.amazonaws.com; MqttPort 443; MqttUser tasmota?x-amz-customauthorizer-name=TasmotaAuth; MqttPassword YknLuSd2tBY2HodwI/7RqA==
+11:28:44 MQT: stat/tasmota_4B3316/RESULT = {"SetOption3":"ON"}
+11:28:44 MQT: stat/tasmota_4B3316/RESULT = {"SetOption103":"ON"}
+11:28:45 MQT: stat/tasmota_4B3316/RESULT = {"MqttHost":"a...........u-ats.iot.eu-central-1.amazonaws.com"}
+11:28:45 MQT: stat/tasmota_4B3316/RESULT = {"MqttPort":443}
+11:28:45 MQT: stat/tasmota_4B3316/RESULT = {"MqttUser":"tasmota?x-amz-customauthorizer-name=TasmotaAuth"}
+11:28:45 MQT: stat/tasmota_4B3316/RESULT = {"MqttPassword":"YknLuSd2tBY2HodwI/7RqA=="}
+11:28:47 APP: Restarting
+

Connect to AWS IoT~

Once the MQTTHost and MQTTPort are configured, Tasmota will try to connect to AWS IoT.

Keep in mind that AWS IoT does not support 'retained' messages. Whatever the 'retained' configuration in Tasmota, messages are always published as 'retained=false'.

Here is an example of output you should see:

00:00:04 HTP: Web server active on sonoff-4585 with IP address 192.168.1.59
+00:00:04 UPP: Multicast (re)joined
+21:28:25 MQT: Attempting connection...
+21:28:25 MQT: AWS IoT endpoint: xxxxxxxxxxxxx-ats.iot.eu-central-1.amazonaws.com
+21:28:26 MQT: AWS IoT connected in 1279 ms
+21:28:26 MQT: Connected
+21:28:26 MQT: tele/tasmota/LWT = Online
+21:28:26 MQT: cmnd/tasmota/POWER =
+21:28:26 MQT: tele/tasmota/INFO1 = {"Module":"Sonoff Basic","Version":"9.0.0.2(sonoff)","FallbackTopic":
+"cmnd/DVES_67B1E9_fb/","GroupTopic":"sonoffs"}
+

7. Check end-to-end communication~

In the AWS IoT console, click on "MQTT test client" in the left panel.

In the "Subscribe to a topic" field, type +/topic/#, topic being your topic name, then click on "Subscribe". This will display all MQTT messages received. Type a command in the Web Tasmota console, you should see MQTT message flow.

Enjoy!

8. Cleaning~

Cleaning: to avoid having CloudFormation templates piling up in your console, you can delete them. The created resources will remain, if you have left the parameter RetentionPolicy to Retain.

9. Troubleshooting~

TLSError shows any error at the TLS level. See here for most common error codes.

For implementation details, see here~

\ No newline at end of file diff --git a/AZ-7798/index.html b/AZ-7798/index.html new file mode 100644 index 0000000000..5721a95042 --- /dev/null +++ b/AZ-7798/index.html @@ -0,0 +1,4 @@ + AZ7798 CO2 meter - Tasmota
Skip to content

AZ7798 CO2 meter~

This feature is not included in precompiled binaries

When compiling your build add the following to user_config_override.h:

#ifndef USE_AZ7798
+#define USE_AZ7798      // Add support for AZ-Instrument 7798 CO2 datalogger (+1k6 code)
+#endif
+

The AZ7798 CO2 Meter/Datalogger appears to be made by AZ Instrument, but other branded versions exist.

The CO2 meter is already provided with a PC interface in the form of a logic-level (3V3) serial port, accessible through a 2.5mm stereo jack on the back. The Wemos D1 Mini is connected to this serial port instead, and the PC interface is no longer available.

Configuration~

Connecting the AZ7798 to a Wemos D1 Mini~

The Wemos D1 Mini is fitted inside the meter enclosure. This requires the enclosure to be opened. There are four screws. Two of the screws are hidden behind the type label. Use the tip of a small knife to cut holes in the label.

There are also two clips at the top of the enclosure. To release those, gently pry apart the bottom of the enclosure a small amount. The grey band between the two halves can now be lifted up and pushed aside a little and expose the clips near the top. A push with a flat-blade screwdriver will then release the clips.

Once the enclosure is opened, disconnect the two serial port wires from the socket on the back panel. The black wire (0V) can remain connected. Now connect these two wires to TX and RX of the Wemos D1 Mini.

Wemos D1 Mini AZ7798
TX wire with stripe
RX wire without stripe

The power supply for the Wemos D1 Mini is taken directly from the power supply for the meter, which is a 5V external power supply. Solder two wires to the incoming power supply and connect the other ends to the 5V and G terminals on the Wemos D1 Mini.

Make a small right-angle bracket for mounting the Wemos D1 Mini to the meter board. Use double-sided tape to hold everything together.

Tasmota Settings~

In the Configuration -> Configure Module page assign:

  1. D1 TX to AZ Tx
  2. D3 RX to AZ Rx

After reboot of the device the AZ7798 measurements are shown.

\ No newline at end of file diff --git a/About/index.html b/About/index.html new file mode 100644 index 0000000000..b5c83e7782 --- /dev/null +++ b/About/index.html @@ -0,0 +1 @@ + About - Tasmota
Skip to content

About

If you don't have the willingness to tinker and learn... TURN BACK!.

Tasmota is not a commercial product and support is limited. You have to be willing to research and solve potential problems yourself.

Tasmota logo

Tasmota is an open source firmware for Espressif ESP8266, ESP32, ESP32-S or ESP32-C3 chipset based devices created and maintained by Theo Arends.

Everything began as Sonoff-MQTT-OTA with a commit on 25th January 2016, by Theo Arends. Its goal was to provide ESP8266 based ITEAD Sonoff devices with MQTT and 'Over the Air' or OTA firmware.

What started as a simple way to hack a cloud bound Sonoff Basic (one of the first cheap and accessible smart home devices in the market) into a locally controlled device has grown into a fully fledged ecosystem for virtually any ESP8266 based device.

Contribute~

Any contribution helps our team and makes Tasmota better for the entire community!

Everybody is welcome and invited to contribute to Tasmota Project by:

  • providing Pull Requests (Features, Proof of Concepts, Language files or Fixes)
  • testing new released features and report issues
  • donating to acquire hardware for testing and implementing or out of gratitude
  • contributing missing documentation for features and devices

Credits~

People helping to keep the show on the road:

  • David Lang providing initial issue resolution and code optimizations
  • Heiko Krupp for his IRSend, HTU21, SI70xx and Wemo/Hue emulation drivers
  • Wiktor Schmidt for Travis CI implementation
  • Thom Dietrich for PlatformIO optimizations
  • Marinus van den Broek for his EspEasy groundwork
  • Pete Ba for more user friendly energy monitor calibration
  • Lobradov providing compile optimization tips
  • Flexiti for his initial timer implementation
  • reloxx13 for his TasmoAdmin management tool
  • Joachim Banzhaf for his TSL2561 library and driver
  • Gijs Noorlander for his MHZ19, SenseAir and updated PubSubClient drivers
  • Erik Montnemery for his HomeAssistant Discovery concept and many code tuning tips
  • Federico Leoni for continued HomeAssistant Discovery support
  • Aidan Mountford for his HSB support
  • Daniel Ztolnai for his Serial Bridge implementation
  • Gerhard Mutz for multiple sensor & display drivers, Sunrise/Sunset, and scripting
  • Nuno Ferreira for his HC-SR04 driver
  • Adrian Scillato for his (security) fixes and implementing and maintaining KNX
  • Gennaro Tortone for implementing and maintaining Eastron drivers
  • Raymond Mouthaan for managing Wemos Wiki information
  • Norbert Richter for his decode-config.py tool
  • Andre Thomas for providing thehackbox OTA support and daily development builds
  • Joel Stein, digiblur and Shantur Rathore for their Tuya research and driver
  • Frogmore42 for providing many issue answers
  • Jason2866 for platformio support and providing many issue answers
  • Blakadder for managing the new document site and providing template management
  • Stephan Hadinger for refactoring light driver, enhancing HueEmulation and Zigbee support
  • tmo for designing the official Tasmota logo
  • Stefan Bode for his Shutter and Deep sleep drivers
  • Jacek Ziółkowski for his TDM management tool and Tasmotizer flashing tool
  • Christian Staars for NRF24L01 and HM-10 Bluetooth sensor support
  • Paul Diem for UDP Group communication support
  • Jörg Schüler-Maroldt for his initial ESP32 port
  • Many more providing Tips, Wips, Pocs, PRs and Donations

License~

This program is licensed under GPL-3.0

\ No newline at end of file diff --git a/Alexa/index.html b/Alexa/index.html new file mode 100644 index 0000000000..70ef5cda1c --- /dev/null +++ b/Alexa/index.html @@ -0,0 +1 @@ + Alexa - Tasmota
Skip to content

Alexa

This feature is included only in tasmota, tasmota32, tasmota-lite and tasmota-sensors binaries

When compiling your build add the following to user_config_override.h: c++ #define USE_EMULATION // Enable Wemo or Hue emulation #define USE_EMULATION_HUE // Enable Hue Bridge emulation for Alexa (+14k code, +2k mem common) #define USE_EMULATION_WEMO // Enable Belkin WeMo emulation for Alexa (+6k code, +2k mem common)

Connecting to Alexa~

You can interact with Tasmota using Amazon Alexa through its Echo devices .

In Configuration - Configure Other page in the webUI select emulation type.

Warning

Alexa Hue integration requires a physical Alexa device. The app alone is not enough.

Check your router settings. You have to enable IGMP

Belkin WeMo Emulation~

Use Belkin WeMo for devices with a single relay or Hue Bridge for devices with one or more relays or for lights. Tasmota devices will be discovered by the Alexa app.

You do not need to install the Wemo app or enable the Wemo skill in Alexa. Just tell Alexa to discover devices of the appropriate type (plug, switch, etc.) and when it asks what brand device, scroll to the end and choose "Other".

Hue Bridge Emulation~

For control of lights, color control (introduced in version 6.5.0.9), on/off, and dimming is supported. Enable Hue Bridge emulation and perform a device discovery in the Alexa app. No skill needs to be added to Alexa. Select Hue Bridge V1 as the device type.

Relays and lights with friendly names beginning with a dollar sign ($) will cause Tasmota to not "announce" them (i.e., be hidden) to Alexa. If they were previously discovered, they will still continue to work. As there are only four friendly names provided in Tasmota, if FriendlyName4 begins with $, component 4 and all higher numbered discoverable components will not be discovered by Alexa.

Hue Bridge emulation does not support sensors.

\ No newline at end of file diff --git a/ArtNet/index.html b/ArtNet/index.html new file mode 100644 index 0000000000..b97b0f75cf --- /dev/null +++ b/ArtNet/index.html @@ -0,0 +1,10 @@ + ArtNet DMX - Tasmota
Skip to content

ArtNet DMX

This feature included in Tasmota32 precompiled binary but not in ESP8266 builds

When compiling your build add the following to user_config_override.h:

#define USE_LIGHT_ARTNET                         // Add support for DMX/ArtNet via UDP on port 6454 (+3.5k code) 
+

ArtNet is a royalty-free UDP based protocol to transport DMX lighting control.It allows to control either a Light, or a WS2812 Matrix/Strip with a remote software with real-time animations.

This feature is experimental, it has been tested with LED Lab (Mac and iOS) and QLC (linux)

When enabling ArtNet mode, Tasmota listens to UDP port 6454 on local address and on multicast address 239.255.25.54. Incoming DMX/ArtNet packets drive the display and light.

ezgif-3-5d1d4b87d0

Important: DMX/ArtNet payload must be sent in GBR format, i.e. 3 bytes in Green/Red/Blue order.

While in ArtNet mode, you can control the brightness with Dimmer and turn off light/display with Power; or with the Web UI. Changing the color has no effect until you stop ArtNet mode.

Quickstart Tutorial~

Step 1. Flash Tasmota32 to ESP32 based device, or compile your own firmware with #define USE_LIGHT_ARTNET for ESP8266 based devices

Step 2. Configure and launche ArtNet.

For M5Stack Atom Matrix or ESP32C3 01Space RGB, the matrix is 5x5 no alternate:

ArtNetConfig {"Rows":5, "Cols":5, "Offset":0, "Alternate":false, "Universe":0} ArtNet Start

For BTF Lighting 8x8 or 16x16 matrix, with alternate lines:

ArtNetConfig {"Rows":8, "Cols":8, "Offset":0, "Alternate":true, "Universe":0} ArtNet Start

Step 3. Download LED Lab from Christopher Schardt for MacOS or iOS

Step 4. Launch LED Lab, click on the Settings icon (wheel), select LED Setup...

In the LED Setup: - Enter the number of rows in Outputs - Enter the number of columns in LEDS/Output - Enter the IP Address of the Tasmota device - Make sure that UDP Broadcast is selected (green background) - Make sure First Universe is 1 and Channel Range starts with 1 - Set Channel Values for Each Pixel to GRB - Click OK in upper right corner

Step 5. Back to the Settings, click on LED Display to start streaming. Your device should start to show LED animations.

Operating modes~

LED matrix~

When setting "Rows":<x> with <x> greater than 1, the display is considered as a LED matrix. The first row matches the Univers number, and following rows are matched to increasing Universe numbers.

Example for M5Stack Atom Matrix or ESP32C3 RGB:

ArtNetConfig {"Rows":5, "Cols":5, "Offset":0, "Alternate":false, "Universe":0}
+ArtNet Start
+

The following are done with Athom LED strip controller (ESP8266), using an external level shifter, and 8x8 Adafruit matrix.

Fast animation: ezgif-3-98953661ef

Linear Strip~

When setting "Rows":1 ArtNet uses the WS2812 as a linear strip, and converts the paylaod to strip values.

Example for M5Stack Atom Matrix or ESP32C3 RGB:

ArtNetConfig {"Rows":1, "Cols":30, "Offset":0, "Universe":0}
+ArtNet Start
+

Single Light~

When setting "Cols":0 (zero columns) ArtNet uses the first GBRWW payload to control the global light (ex PWM) or the entire WS2812 strip. Multiple single lights (eg. bulbs) can be used on the same universe by setting the offset value.

Example:

ArtNetConfig {"Cols":0, "Universe":0, "Offset":0}
+ArtNet Start
+

The calibration values set by the RGBWWTable command will be applied to any DMX inputs. For example to disable the white channels:

RGBWWTable 255,255,255,0,0
+

Commands~

Command Description
ArtNetConfig <json> Example ArtNetConfig {"Rows":5, "Cols":5, "Offset":0, "Alternate":false, "Universe":0}
There are two modes for ArtNet configuration: simple light or adressable leds.
In simple Light mode, "cols" is zero. Only "Universe" needs to be specified.
Example: ArtNetConfig {"Cols":0, "Universe":0}
In Adressable Light mode, all parameters can be specified.

Rows: number of rows of display, 1 for light mode or single strip
Cols: number of columns of the display, or length of the strip, or 0 for single light
"Offset": number of adressable leds to skip
Alternate: (true/false) indicates that every other line is reversed (common in large matrix)
Universe: starting DMX Universe number for the first line (0 based so you may need to substract 1 from software)
ArtNet Start, On>, or1: Start ArtNet mode, listen to UDP port 6454 and forceSetOption148 1for autorun at restart<BR>Stop,Offor0: Stop ArtNet mode, close UDP port and forceSetOption148 0` to disable ArtNet mode at restart
SetOption148 Enables or disables autorun of ArtNet mode at start. If for any reason listening to UDP port fails, this mode is disabled

Troubleshooting~

Note: ArtNet mode is not compatible with Fade or Scheme.

When ArtNet mode is enabled, packet metrics are added to Teleperiod or can be shown with Status 10.

16:47:42.981 RSL: SENSOR = {"Time":"2022-11-12T16:47:42","ArtNet":{"PacketsReceived":40149,"PacketsAccepted":40149,"Frames":8272,"PacketsPerRow":[8030,8030,8030,8030,8029]}}
+
ArtNet metric Description
PacketsReceived Number of UDP packet received on port 6454
PacketsAccepted Number of packets that are valid ArtNet packets (signature is ok)
Frames Number of complete frames displayed (i.e. several packets were aggregated to a single frame)
PacketsPerRow Array of number of packets processed for each row; this can indicate packet loss

ArtNetConfig parameters are pesisted in the following Settings:

ArtNet parameter Stored in
Cols Stored in StepPixels
Rows Stored in Pixels containing Rows*Cols
Offset Stored in Rotation
Atl Stored in SetOption16 (counter/clock-wise)
Universe Stored in a new Settings parameter

Limitations~

The display size is currently limite to 30x30 leds due to RAM and size constraints. It should be possible to go beyond this size with custom builds.

The display was only tested with LED Lab from Christopher Schardt

LED payload must be formed by a series of 3-bytes for WS2812 or 4-bytes for SK6812 in the following format: GRB for WS2812 and RGBW for SK6812.

Multicast is currently untested.

\ No newline at end of file diff --git a/Azure-IoT-Central/index.html b/Azure-IoT-Central/index.html new file mode 100644 index 0000000000..c59bf55414 --- /dev/null +++ b/Azure-IoT-Central/index.html @@ -0,0 +1,13 @@ + Azure IoT Central - Tasmota
Skip to content

Azure IoT Central

!!! failure "This feature is not included in precompiled binaries, To use it you must compile your build.

Add the following to user_config_override.h:

#ifndef USE_MQTT_TLS
+#define USE_MQTT_TLS
+#endif
+#define USE_MQTT_AZURE_IOT
+#define USE_MQTT_AZURE_DPS_SCOPEID        "YOURSCOPEIDHERE"
+#define USE_MQTT_AZURE_DPS_PRESHAREDKEY   "YOURPRESHAREDKEYHERE=="
+

As of Tasmota version 9.2.4, Tasmota now supports TLS 1.2 connections to Azure IoT Hub and IoT Central using time bound token authentication based on a unique key. IoT Central includes Azure Device Provisioning Service which is used for simplified deployment at scale.

Benefits~

Azure IoT Central supports supports bi-directional communication between the could and both IoT Devices and/or Intelligent Edge devices base on Azure IoT Edge and is delivered as a Software as a Service (SaaS). In addition to telemtary and communication, IoT Central if a full solution for quickly managing and deploying IoT solutions at scale.

Authentication~

This version of Tasmota leverages the Preshared Key authentication, which will create a time bound (one hour by default) SHA256 signature based on a unique key. Only this signed text is sent across the network over a TLS 1.2 channel ensuring mutual authentication. Because of this time bound nature, Tasmota must (by default) be configured to synchronize time with public Network Time Protocol Servers. Developer level information provided here discussed here.

Cost~

Azure IoT Central provides a free tier that allows for 2 devices at no charge.

Get Started~

0. Create an IoT Central Application~

Follow the steps here to create an IoT Central Application: https://docs.microsoft.com/en-us/azure/iot-central/core/quick-deploy-iot-central

1. Get the ID Scope~

Select Administration --> Device Connection to note the ID Scope as shown below:

image

2. Create an Enrollment Group~

Click Create enrollment group, name the group, and select the Attestation type as Shared access signature (SAS). Click Save to and copy the Primary Key, as shown below:

image

3. Compile your binary including support for Azure IoT Device Provisioning Service (and IoT Central)~

Following the directions here: https://tasmota.github.io/docs/Compile-your-build/. Compile your binary adding the following settings to your my_user_config.h adding your Scope Id and your Primary Key.

Tip

Don't forget your WiFi for complete automation.

#ifndef USE_MQTT_TLS
+#define USE_MQTT_TLS
+#endif
+#define USE_MQTT_AZURE_IOT
+#define USE_MQTT_AZURE_DPS_SCOPEID        "0ne002AEBBA"
+#define USE_MQTT_AZURE_DPS_PRESHAREDKEY   "iQ9Qtjfux9wWW0guHi/ChRGQX//LLkZEnyUNIaLD8+imAsKvpZwYYT8M0kFPVxt3KjtRF00KpNi5/ejBt+1YLA=="
+

4. Flash your device and configure for WiFi~

Tip

If you configured the WiFi in the config file, no WiFi configuration needed.

Flash your device as discussed here and then configure the WiFi as discussed here.

5. Verify your automatic (DPS) configuration~

In the Console of Tasmota, you will see it was registered with Device Provisioning Service (which created the IoT Hub device) and authenticated with a token, connected and it will start sending state:

image

If you look at the properties, you will see it automatically defined the Host name, port, TLS and MQTT Client (device Id) -- which defaulted to the MAC address:

image

In the IoT Central Application, you can see the telemtry data:

image

\ No newline at end of file diff --git a/Azure-IoT-Hub/index.html b/Azure-IoT-Hub/index.html new file mode 100644 index 0000000000..6b9f0d4422 --- /dev/null +++ b/Azure-IoT-Hub/index.html @@ -0,0 +1,21 @@ + Azure IoT Hub - Tasmota
Skip to content

Azure IoT Hub

!!! failure "This feature is not included in precompiled binaries, To use it you must compile your build.

Add the following to user_config_override.h:

#ifndef USE_MQTT_TLS
+#define USE_MQTT_TLS
+#endif
+#define USE_MQTT_AZURE_IOT
+

As of Tasmota version 9.2.4, Tasmota now supports TLS 1.2 connections to Azure IoT Hub using time bound token authentication based on a unique key. Tasmota can be used with or without Azure Device Provisioning Service which is used for simplified deployment at scale.

Benefits~

Azure IoT Hub supports bi-directional communication between the could and both IoT Devices and/or Intelligent Edge devices base on Azure IoT Edge. Azure IoT Hub supports REST, AMQP and the MQTT 3.1.1 protocol as discussed here. IoT Hub supports Trusted Platform Module (when used with Device Provisioning Service), x509 Certificates and Preshared Keys authentication discussed here.

Authentication~

This version of Tasmota leverages the Preshared Key authentication, which will create a time bound (one hour by default) SHA256 signature based on a unique key. Only this signed text is sent across the network over a TLS 1.2 channel ensuring mutual authentication. Because of this time bound nature, Tasmota must (by default) be configured to synchronize time with public Network Time Protocol Servers. Developer level information provided here discussed here.

Cost~

Azure IoT Hub provides a free tier that allows for up to 8,000 message a day.

IoT Central~

In addition to Azure IoT Hub which is a Platform as a Service (PaaS), Tasmota also works with IoT Central which is a more complete Software as a Service (SaaS).

How to configure (without Device Provisioning Service)~

0. Open an Azure Subscription~

If you don't already have an Azure Subscription (one is included with most MSDN subscriptions), you can get started here: https://azure.microsoft.com/account/free

1. Create an Azure IoT Hub and a Device~

The following steps will walk you through creating an IoT Hub and your first device using a web browser: https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-create-through-portal

You will need 3 pieces of information from this step:

Information Your Setting Example Value
IoT Hub Full Name myiothub.azure-devices.net
Device Id myfirstTasmotaDevice
Primary Key i2B6TVRnpWGS5i5aZaRddaGTc+tIte1gg4PUkh0t+30=

2. Compile your binary including support for Azure IoT~

Following the directions here: https://tasmota.github.io/docs/Compile-your-build/ compile your binary adding the following settings to your my_user_config.h.

#ifndef USE_MQTT_TLS
+#define USE_MQTT_TLS
+#endif
+#define USE_MQTT_AZURE_IOT
+

3. Flash your device and configure for WiFi~

Flash your device as discussed here and then configure the WiFi as discussed here.

4. Configure your device for you Azure IoT Hub~

In the web portal, click the configuration button and then the configure MQTT button.

Following the diagram below, set the: Host (IoT Hub Full Name), Port of 8883, select MQTT TLS, type in the Client and User (your Device Id) and the Password (your Primary Key) and click Save. Your device will reboot and connect.

image

5. Verify the connection~

In the Console of Tasmota, you will see it was authenticated with a token, connected and it will start sending state:

image

Using a tool like the Azure IoT Explorer, you can see message received in IoT Hub. Note the topic property.

image

6. Send a message to your Tasmota~

Using a tool like the Azure IoT Explorer, select Cloud-to-device message set a property of Topic to /power, add toggle to the message body and click Send message to device button. All of the Tasmota Commands are discussed here https://tasmota.github.io/docs/Commands/.

image

How to configure (with Device Provisioning Service)~

Azure Device Provisioning Services (DPS) allows for automatic deployment at scale. Simular to Tasmota authenticating to IoT Hub without DPS, authenticating to DPS uses a time bound SHA256 signature to authenticate over TLS 1.2.

For this setup, we configure the following settings when building our binary. For full automation, you will want to add STA_SSID1 and STA_PASS1 to your user_config_override.h.

Add the following to user_config_override.h:

#ifndef USE_MQTT_TLS
+#define USE_MQTT_TLS
+#endif
+#define USE_MQTT_AZURE_IOT
+#define USE_MQTT_AZURE_DPS_SCOPEID        "YOURSCOPEIDHERE"
+#define USE_MQTT_AZURE_DPS_PRESHAREDKEY   "YOURPRESHAREDKEYHERE=="
+

0. Open an Azure Subscription~

If you don't already have an Azure Subscription (one is included with most MSDN subscriptions), you can get started here: https://azure.microsoft.com/account/free

Following: https://docs.microsoft.com/en-us/azure/iot-dps/tutorial-set-up-cloud to create your DPS and IoT Hub. Note your Scope Id for later use as shown below:

image

2. Create a Group Enrollment with a Symmetrical Key~

In your Device Provisiniong Service, click Manage enrollments and then click Add enrollment group.

As shown below, name your group, select Symmetric Key, select your linked IoT Hub and then click Save.

image

Once created you can select the enrollment group to retrieve the Primary Key, as shown below:

image

3. Compile your binary including support for Azure IoT Device Provisioning Service~

Following the directions here: https://tasmota.github.io/docs/Compile-your-build/ compile your binary adding the following settings to your my_user_config.h adding your Scope Id and your Primary Key.

!!! tip Don't forget your WiFi settings for complete automation.

#ifndef USE_MQTT_TLS
+#define USE_MQTT_TLS
+#endif
+#define USE_MQTT_AZURE_IOT
+#define USE_MQTT_AZURE_DPS_SCOPEID        "0ne00223A39"
+#define USE_MQTT_AZURE_DPS_PRESHAREDKEY   "mVVdX8MPUFUoYaG7Wq6HyMcsz0kZNfwVwiafChvFDxrs0s8pa5gVV6myMbqOBCqGraVFZFupD3RiIgx0B4ZACA=="
+

4. Flash your device and configure for WiFi~

!!! tip If you configured the WiFi in the config file, no WiFi configuration needed.

Flash your device as discussed here and then configure the WiFi as discussed here.

5. Verify your automatic (DPS) configuration~

In the Console of Tasmota, you will see it was registered with Device Provisioning Service (which created the IoT Hub device) and authenticated with a token, connected and it will start sending state:

image

If you look at the Information, you will see it automatically defined: Host, Port, TLS and MQTT Client (device Id) -- which defaulted to the MAC address which is unique:

image

Using a tool like the Azure IoT Explorer, you can see message received in IoT Hub. Note the topic property.

image

6. Send a message to your Tasmota~

Using a tool like the Azure IoT Explorer, select Cloud-to-device message set a property of Topic to /power, add toggle to the message body and click the Send message to device button.

image

\ No newline at end of file diff --git a/BH1750/index.html b/BH1750/index.html new file mode 100644 index 0000000000..d39707eabd --- /dev/null +++ b/BH1750/index.html @@ -0,0 +1,5 @@ + BH1750 ambient light sensor - Tasmota
Skip to content

BH1750 ambient light sensor~

This feature is included only in tasmota32 and tasmota-sensors binaries

When compiling your build add the following to user_config_override.h:

#ifndef USE_BH1750
+#define USE_BH1750      // [I2cDriver11] Enable BH1750 sensor (I2C address 0x23 or 0x5C) (+0k5 code)
+#endif
+

The BH1750 is a digital ambient light sensor module which uses I2C to communicate. The BH1750 provides you with a digital value in lux (Lx) over a range of 1 - 65535 lx. See datasheet for more information.

Configuration~

Wiring~

BH1750 ESP
GND GND
VCC 3.3V
SDA GPIOx
SCL GPIOy

Tasmota Settings~

In the Configuration -> Configure Module page assign:

  1. GPIOx to I2C SDA
  2. GPIOy to I2C SCL

After a reboot the driver will detect BH1750 automatically and display Illuminance.

image

Sensor sends a tele/%topic%/SENSOR JSON response:

{"Time":"2019-11-03T20:45:37","BH1750":{"Illuminance":79}}
+

Commands~

Command Parameters
Bh1750Resolution resolution mode. x = sensor number (1..2)
0..2 = choose sensor resolution (0 = high (default), 1 = high2, 2 = low)
Bh1750MTime Measurement Time value. x = sensor number (1..2)
31..254 = set measurement time (default = 69)

Breakout Boards~

\ No newline at end of file diff --git a/BME280/index.html b/BME280/index.html new file mode 100644 index 0000000000..382997f215 --- /dev/null +++ b/BME280/index.html @@ -0,0 +1,14 @@ + BME280 temperature, humidity and pressure sensor - Tasmota
Skip to content

BME280 temperature, humidity and pressure sensor~

This feature is included only in tasmota-sensors and tasmota32 binaries

When compiling your build add the following to user_config_override.h:

#ifndef USE_BMP
+#define USE_BMP     // [I2cDriver10] Enable BMP085/BMP180/BMP280/BME280 sensors (I2C addresses 0x76 and 0x77) (+4k4 code)
+#endif
+

BME280 sensor, an environmental sensor with temperature, barometric pressure and humidity" See BME280 Temperature, Humidity and Pressure Sensor for more information.

BME280 driver also supports BMP085, BMP180 and BMP280 pressure sensors.

Configuration~

Wiring~

BME280 ESP
GND GND
VCC 3.3V
SDA GPIOx
SCL GPIOy

Tasmota Settings~

In the Configuration -> Configure Module page assign:

  1. GPIOx to I2C SDA
  2. GPIOy to I2C SCL

Note

If you are using breakout boards which break out pins CSB please ensure that you connect this pin to VCC to ensure that the chip stays in I2C mode. This is due to some manufacturers of breakout boards add pull-up resistors to allow for SPI compatibility (some would default to SPI, others to I2C)_ Connect the SDO pin to GND or VCC to ensure required I2C address is used by the chip - Again some manufacturers add resistors to provide external bias but due to them wanting to retain SPI compatibility such pulldown resistors may not be sufficient to ensure that the pin levels are at the correct transition during power-up / reset state._ Since the chip supports both SPI and I2C, but in Tasmota only I2C is supported you want to make sure that the chip is in I2C mode during reset/power-on and remain in that state during operation otherwise unexpected behaviour may result when the Tasmota firmware polls the chip for sensor data.

Sonoff Basic wiring

After a reboot the driver will detect BME280 automatically and display temperature, humidity and pressure measured.

Sensor sends a tele/%topic%/SENSOR JSON response:

{
+  "Time": "2019-11-03T19:34:28",
+  "BME280": {
+    "Temperature": 21.7,
+    "Humidity": 66.6,
+    "Pressure": 988.6
+  },
+  "PressureUnit": "hPa",
+  "TempUnit": "C"
+}
+

From v6.1.2.20 you can use multiple BMP Sensors (#4195)

Limit is 2 sensors and you have to change the address of one sensor.
Than connect your sensor SCL and SDA parallel to the device.

BME280 address change example:

Commands~

TempOffset can be used for calibrating the measured temperature. This setting affects all temperature sensors on the device.

Breakout boards~

Do NOT buy Breakout boards which supports 5V too. The onboard vreg will heat the PCB and you get false too high readings

\ No newline at end of file diff --git a/BME680/index.html b/BME680/index.html new file mode 100644 index 0000000000..fd76fe17c3 --- /dev/null +++ b/BME680/index.html @@ -0,0 +1,16 @@ + BME680 temperature, humidity, pressure and gas sensor - Tasmota
Skip to content

BME680 temperature, humidity, pressure and gas sensor~

This feature is included only in tasmota-sensors and tasmota32 binaries

When compiling your build add the following to user_config_override.h:

#ifndef USE_BME68X
+#define USE_BME68X      // Enable support for BME680/BME688 sensor using Bosch BME68x library (+6k9 code)
+#endif
+

BME680 sensor, an environmental I2C sensor with temperature, humidity, barometric pressure and gas.

The BME680 takes those sensors to the next step in that it contains a small MOX sensor. The heated metal oxide changes resistance based on the volatile organic compounds (VOC) in the air, so it can be used to detect gasses & alcohols such as Ethanol, Alcohol and Carbon Monoxide, and perform air quality measurements. Note it will give you one resistance value, with overall VOC content, but it cannot differentiate gasses or alcohols.

See BME680 - Temperature, Humidity, Pressure and Gas Sensor for more information.

Configuration~

Wiring~

BME680 ESP
GND GND
VCC 3.3V
SDA GPIOx
SCL GPIOy

Tasmota Settings~

In the Configuration -> Configure Module page assign:

  • GPIOx to I2C SDA
  • GPIOy to I2C SCL

After a reboot the driver will detect BME680 automatically and display temperature, humidity, dew point, pressure and gas resistance measured.

image

Sensor sends a tele/%topic%/SENSOR JSON response:

{
+  "Time": "2021-09-22T17:00:00",
+  "BME680": {
+    "Temperature": 24.5,
+    "Humidity": 33.0,
+    "DewPoint": 7.1,
+    "Pressure": 987.7,
+    "Gas": 1086.43
+  },
+  "PressureUnit": "hPa",
+  "TempUnit": "C"
+}
+

Commands~

  • TempOffset can be used for calibrating the measured temperature. This setting affects all temperature sensors on the device.
  • TempRes sets temperature resolution.
  • SetOption8 sets temperature unit in Celsius or in Fahrenheit.
  • SetOption24 sets pressure unit in hPa or in mmHg.

Breakout Boards~

Buy from: - Adafruit - AliExpress

\ No newline at end of file diff --git a/BMP280/index.html b/BMP280/index.html new file mode 100644 index 0000000000..dbafa27cef --- /dev/null +++ b/BMP280/index.html @@ -0,0 +1,11 @@ + + + + + + + + +Redirecting... + + diff --git a/BUILDS/index.html b/BUILDS/index.html new file mode 100644 index 0000000000..df00987db8 --- /dev/null +++ b/BUILDS/index.html @@ -0,0 +1 @@ + BUILDS - Tasmota
Skip to content

BUILDS

Available Features and Sensors~

ESP8266 / ESP32

l = lite, t = tasmota, k = knx, s = sensors, i = ir, d = display

Note: minimal variant is not listed as it shouldn't be used outside of the upgrading process.

Feature or Sensor l t k s i d Remarks
MY_LANGUAGE en_GB x x / x x x x x
USE_IMPROV x x / x x x x x
USE_UFILESYS - - / x - - - -
USE_ARDUINO_OTA - - / - - - - -
USE_DOMOTICZ - x / x x x x -
USE_HOME_ASSISTANT - - / - - - - -
USE_TASMOTA_DISCOVERY x x / x x x x x
USE_MQTT_TLS* - - / x - - - -
USE_MQTT_AWS_IOT - - / - - - - -
USE_4K_RSA - - / - - - - -
USE_TELEGRAM - - / - - - - -
USE_KNX - - / x x - - -
USE_WEBSERVER x x / x x x x x
USE_WEBSEND_RESPONSE - - / - - - - -
USE_EMULATION_HUE x x / x - x - -
USE_EMULATION_WEMO x x / x - x - -
USE_DISCOVERY - - / - - - - -
WEBSERVER_ADVERTISE - x / - x - - x
MQTT_HOST_DISCOVERY - - / - - - - -
USE_TIMERS x x / x x x x x
USE_TIMERS_WEB x x / x x x x x
USE_SUNRISE x x / x x x x x
USE_RULES x x / x x x x x
USE_SCRIPT - - / - - - - -
USE_EXPRESSION - - / - - - - -
SUPPORT_IF_STATEMENT - - / - - - - -
USE_HOTPLUG - - / - - - - -
USE_PROMETHEUS - - / - - - - -
USE_PING - - / - - - - -
Feature or Sensor l t k s i d Remarks
ROTARY_V1 - x / x - x - -
USE_SONOFF_RF - x / - x x - -
USE_RF_FLASH - x / - x x - -
USE_SONOFF_SC - x / - x x - -
USE_TUYA_MCU x x / - x x - x
USE_ARMTRONIX_DIMMERS - x / - x - - -
USE_PS_16_DZ - x / - x - - -
USE_SONOFF_IFAN - x / - x - - -
USE_BUZZER - x / x x x - -
USE_ARILUX_RF - x / - x - - -
USE_SHUTTER - x / x x - - -
USE_DEEPSLEEP - x / x - x - -
USE_EXS_DIMMER - x / - x - - -
USE_DEVICE_GROUPS - x / x - - - -
USE_PWM_DIMMER - x / - x - - -
USE_KEELOQ - - / - - - - -
USE_SONOFF_D1 - x / - x - - -
USE_SHELLY_DIMMER - x / - - - - -
USE_AC_ZERO_CROSS_DIMMER - x / x x x x x
Feature or Sensor l t k s i d Remarks
USE_LIGHT x x / x x x x x
USE_WS2812 - x / x x x - x
USE_WS2812_DMA - - / - - - - -
USE_MY92X1 - x / - x x - x
USE_SM16716 - x / - x x - x
USE_SM2135 - x / - x x - x
USE_SM2335 - x / - x x - x
USE_BP5758D - x / - x x - x
USE_BP1658CJ - x / - x x - x
USE_SONOFF_L1 - x / - x x - x
USE_ELECTRIQ_MOODL - x / - x x - x
USE_ENERGY_SENSOR - x / x x x - -
USE_ENERGY_DUMMY - x / x x x - -
USE_PZEM004T - x / x x x - -
USE_PZEM_AC - x / x x x - -
USE_PZEM_DC - x / x x x - -
USE_MCP39F501 - x / - x x - -
USE_SDM72 - - / x - x - -
USE_SDM120 - - / x - x - -
USE_SDM230 - - / x - - - -
USE_SDM630 - - / x - x - -
USE_DDS2382 - - / x - x - -
USE_DDSU666 - - / x - x - -
USE_SOLAX_X1 - - / - - - - -
USE_LE01MR - - / - - - - -
USE_BL09XX - x / x x x - -
USE_TELEINFO - - / - - - - -
USE_IEM3000 - - / - - - - -
USE_WE517 - - / x - - - -
USE_MODBUS_ENERGY - - / x - - - -
USE_ADC_VCC x - / - - - x -
USE_COUNTER - x / x x x - x
USE_DS18x20 - x / x x x - x
USE_DHT - x / x x x - x
USE_MAX31855 - - / x - x - -
USE_MAX31865 - - / - - - - -
USE_THERMOSTAT - - / - - - - -
USE_LMT01 - - / x - x - -
Feature or Sensor l t k s i d Remarks
USE_I2C - x / x x x - x
USE_SHT - - / x - x - -
USE_HTU - - / x - x - -
USE_BMP - - / x - x - -
USE_BME68X - - / x - x - -
USE_BH1750 - - / x - x - -
USE_VEML6070 - - / x - x - -
USE_ADS1115 - - / x - x - -
USE_INA219 - - / x - x - -
USE_INA226 - - / - - - - -
USE_SHT3X - - / x - x - -
USE_TSL2561 - - / - - - - -
USE_TSL2591 - - / - - - - -
USE_MGS - - / x - x - -
USE_SGP30 - - / x - x - -
USE_SGP40 - - / x - x - -
USE_SEN5X - - / x - x - -
USE_SI1145 - - / - - - - -
USE_LM75AD - - / x - x - -
USE_APDS9960 - - / - - - - -
USE_MCP230xx - - / - - - - -
USE_PCA9632 - - / - - - - -
USE_PCA9685 - - / - - - - -
USE_MPR121 - - / - - - - -
USE_CCS811 - - / - - x - -
USE_CCS811_V2 - - / x - - - -
USE_MPU6050 - - / - - - - -
USE_DS3231 - - / - - - - -
USE_MGC3130 - - / - - - - -
USE_MAX44009 - - / - - - - -
USE_SCD30 - - / x - x - -
USE_SCD40 - - / x - - - -
USE_SPS30 - - / - - - - -
USE_ADE7880 - - / - - - - -
USE_ADE7953 - x / x x x - x
USE_VL53L0X - - / x - x - -
USE_VL53L1X - - / - - - - -
USE_MLX90614 - - / - - - - -
USE_CHIRP - - / - - - - -
USE_PAJ7620 - - / - - - - -
USE_PCF8574 - - / - - - - -
Feature or Sensor l t k s i d Remarks
USE_HIH6 - - / x - x - -
USE_DHT12 - - / x - x - -
USE_DS1624 - - / x - x - -
USE_AHT1x - - / - - - - -
USE_HDC1080 - - / - - - - -
USE_WEMOS_MOTOR_V1 - - / x - x - -
USE_IAQ - - / x - x - -
USE_AS3935 - - / x - x - -
USE_VEML6075 - - / - - - - -
USE_VEML7700 - - / - - - - -
USE_MCP9808 - - / - - - - -
USE_MLX90640 - - / - - - - -
USE_HP303B - - / - - - - -
USE_EZOCO2 - - / - - - - -
USE_EZODO - - / - - - - -
USE_EZOEC - - / - - - - -
USE_EZOFLO - - / - - - - -
USE_EZOHUM - - / - - - - -
USE_EZOO2 - - / - - - - -
USE_EZOORP - - / - - - - -
USE_EZOPH - - / - - - - -
USE_EZOPMP - - / - - - - -
USE_EZOPRS - - / - - - - -
USE_EZORGB - - / - - - - -
USE_EZORTD - - / - - - - -
USE_SEESAW_SOIL - - / - - - - -
USE_TOF10120 - - / - - - - -
USE_BM8563 - - / - - - - -
USE_AM2320 - - / - - - - -
USE_T67XX - - / - - - - -
USE_HM330X - - / - - - - -
USE_HDC2010 - - / - - - - -
USE_PCF85363 - - / - - - - -
USE_DS3502 - - / - - - - -
USE_HYT - - / - - - - -
USE_LUXV30B - - / - - - - -
USE_HMC5883L - - / - - - - -
USE_QMC5883L - - / - - - - -
Feature or Sensor l t k s i d Remarks
USE_SPI - - / - - - - x
USE_RC522 - - / - - - - -
USE_CANSNIFFER - - / - - - - -
USE_MHZ19 - - / x - x - -
USE_SENSEAIR - - / x - x - -
USE_PMS5003 - - / x - x - -
USE_NOVA_SDS - - / x - x - -
USE_HPMA - - / x - x - -
USE_SERIAL_BRIDGE - x / x x x - x
USE_MODBUS_BRIDGE - - / x - - - -
USE_MP3_PLAYER - - / x - x - -
USE_AZ7798 - - / - - - - -
USE_PN532_HSU - - / x - x - -
USE_RDM6300 - - / x - x - -
USE_IBEACON - - / x - x - -
USE_GPS - - / - - - - -
USE_HM10 - - / - - x - -
USE_HRXL - - / x - x - -
USE_TASMOTA_CLIENT - - / - - - - -
USE_OPENTHERM - - / - - - - -
USE_MIEL_HVAC - - / - - - - -
USE_PROJECTOR_CTRL - - / - - - - -
USE_AS608 - - / - - - - -
USE_LD2410 - - / - - - - -
USE_TCP_BRIDGE - - / - - - - - zbbridge / zbbrdgpro
USE_NRF24 - - / - - - - -
USE_MIBLE - - / - - - - -
USE_ZIGBEE - - / - - - - -
USE_ZIGBEE_ZNP - - / - - - - -
USE_ZIGBEE_EZSP - - / - - - - - Sonoff ZbBridge
USE_IR_REMOTE - x / - x x x x
USE_IR_RECEIVE - x / - x x x x
USE_IR_REMOTE_FULL - - / - - - x - Enable ALL protocols
USE_SR04 - - / - - x - -
USE_ME007 - - / - - - - -
USE_DYP - - / - - - - -
USE_TM1638 - - / x - x - -
USE_HX711 - - / x - x - -
USE_TX2x_WIND_SENSOR - - / - - - - -
USE_WINDMETER - - / - - - - -
USE_RC_SWITCH - - / x - x - -
USE_RF_SENSOR - - / x - x - - AlectoV2 only
USE_HRE - - / x - x - -
USE_A4988_STEPPER - - / - - - - -
USE_NEOPOOL - - / - - - - -
USE_FLOWRATEMETER - - / - - - - -
Feature or Sensor l t k s i d Remarks
USE_DISPLAY - - / - - - - x
USE_DISPLAY_LCD - - / - - - - x
USE_DISPLAY_SSD1306 - - / - - - - x
USE_DISPLAY_MATRIX - - / - - - - x
USE_DISPLAY_SH1106 - - / - - - - x
USE_DISPLAY_ILI9341 - - / - - - - x
USE_DISPLAY_EPAPER_29 - - / - - - - x
USE_DISPLAY_EPAPER_42 - - / - - - - x
USE_DISPLAY_SSD1351 - - / - - - - x
USE_DISPLAY_RA8876 - - / - - - - x
USE_DISPLAY_ST7789 - - / - - - - x
USE_DISPLAY_TM1637 - - / - - - - x
USE_DISPLAY_TM1621_SONOFF - - / x - - - -
USE_DISPLAY_TM1650 - - / - - - - -
USE_FT5206 - - / - - - - -
USE_FTC532 - - / - - - - -
USE_BS814A2 - - / - - - - -
ESP32 Feature l t k s i d Remarks
USE_HALLEFFECT / x
USE_MI_ESP32 / x See SetOption115
USE_IBEACON_ESP32 / -
USE_WEBCAM / -
USE_ETHERNET / x
USE_I2S_AUDIO / -
USE_TTGO_WATCH / -
USE_SONOFF_SPM / x
USE_DISPLAY_TM1621_SONOFF / x
USE_SHELLY_PRO / x
USE_DALI / -
USE_DINGTIAN_RELAY / -
  • USE_MQTT_TLS is enabled by default in every ESP32 variants
\ No newline at end of file diff --git a/Berry-Cookbook/index.html b/Berry-Cookbook/index.html new file mode 100644 index 0000000000..19d37470d8 --- /dev/null +++ b/Berry-Cookbook/index.html @@ -0,0 +1,500 @@ + Berry Cookbook - Tasmota
Skip to content

Berry Cookbook~

Adding commands to Tasmota~

It is very easy to dynamically add a command to Tasmota with Berry.

Trivial example~

Let's start with the most simple command.

Let's define a command BrGC that triggers a garbage collection and returns the memory allocated by Berry. We first define the function:

def br_gc()
+  var allocated = tasmota.gc()    #- trigger gc and return allocated memory -#
+  import string
+  tasmota.resp_cmnd(string.format('{"BrGc":%i}', allocated))
+end
+

And register the function:

tasmota.add_cmd('BrGc', br_gc)
+

Then in Tasmota Console:

brgc
+
+21:04:30.369 CMD: brgc
+21:04:30.376 RSL: stat/tasmota_923B34/RESULT = {"BrGc":5767}
+

General form of the custom command function~

The custom command function have the general form below where parameters are optionals:

def function_name(cmd, idx, payload, payload_json)
+  ...
+end
+
Parameter Description
cmd string name of the command in lower case. Can be used if same function is used for multiple similar commands for example.
idx Command's index is the unsigned integer (optionally) added at the end of the command name before the parameters (like Demo1). Default to 1 if not specified.
payload string of the command line as without any parsing.
payload_json if the payload is a valid JSON, it is converted into a Berry map object.

More complete example~

In this example, we will create a new command called LightGold that turns the light on and sets it to color gold #FFD700. This command accepts an optional JSON payload with the argument Dimmer ranging from 0..100.

First we define a new Berry function with the logic. This function takes 4 arguments:

  1. cmd: the command name (with same case as it was registered). This is useful if you want to share the same code in multiple commands. Here cmd is LightGold
  2. idx: the command index used, default to 1.
  3. payload: the raw payload of the command as string
  4. payload_json: the payload parsed as JSON, or nil if the payload is not JSON

Example:

  • command lightgold: cmd=LightGold, idx=1, payload="", payload_json=nil
  • command LIGHTGOLD2: cmd=LightGold, idx=2, payload="", payload_json=nil
  • command lightgold not sure: cmd=LightGold, idx=1, payload='not sure', payload_json=nil
  • command lightgold {"value":"some"}: cmd=LightGold, idx=1, payload='{"value":"some"}', payload_json={'value':'some'}

In Berry, arguments are always optional, so you don't need to define them if you don't need them.

def light_gold(cmd, idx, payload, payload_json)
+  var dimmer = 50      #- default brightness to 50% -#
+  var bri
+
+  # parse payload
+  if payload_json != nil && payload_json.find("Dimmer") != nil    # does the payload contain a 'dimmer' field
+    dimmer = int(payload_json.find("Dimmer"))
+  end
+
+  # set_light expects a brightness in range 0..255
+  bri = tasmota.scale_uint(dimmer, 0, 100, 0, 255)
+
+  # build the payload for set_light
+  var light_payload = {'power':true, 'rgb':'FFD700', 'bri':bri}
+
+  #- set the light values -#
+  tasmota.set_light(light_payload)
+
+  # report the command as successful
+  tasmota.resp_cmnd_done()
+end
+

Finally you need to register the command:

tasmota.add_cmd('LightGold', light_gold)
+

Example (in Tasmota console, not Berry console):

lightgold
+
+20:53:28.142 CMD: lightgold
+20:53:28.151 RSL: stat/tasmota_923B34/RESULT = {"POWER":"ON"}
+20:53:28.153 RSL: stat/tasmota_923B34/POWER = ON
+20:53:28.160 RSL: stat/tasmota_923B34/RESULT = {"LightGold":"Done"}
+
+lightgold {"Dimmer":20}
+
+20:54:16.837 CMD: lightgold {"Dimmer":20}
+20:54:16.848 RSL: stat/tasmota_923B34/RESULT = {"LightGold":"Done"}
+

Responding to commands~

Tasmota expects that you send a response to commands. You can use the following methods:

  • tasmota.resp_cmnd_done(): report command as Done (including translated versions)
  • tasmota.resp_cmnd_error(): report command as Error
  • tasmota.resp_cmnd_failed(): report command as Failed
  • tasmota.resp_cmnd_str(<msg>): report an arbitrary string
  • tasmota.resp_cmd(<json>): report a custom JSON message (not prefixed by command name).

Adding a button to the Main menu~

Adding a button to the e.g. main menu can be achieved by using the message type web_add_main_button().

The method to be performed, when the user clicks the button is achieved by using the web_sensor() method checking for the presence of an argument and a possible value assigned to the argument. The class provides the necessary methods to read the arguments:

  • webserver.has_arg(arg_name:string): -> boolean, checks if an argument with this name exists
  • webserver.arg_size(): -> integer, returns the number of arguments
  • webserver.arg(arg_name:string or arg_index:int): -> string, returns the value of the argument either by name or by position number [0..arg_size()-1]. If an argument has multiple values, you need to iterate using ints to get all values
  • webserver.arg_name(arg_index:int) -> string, get the name of argument by index [0..arg_size()-1]

Additionally the webserver class provides a new function of sending information to the Web UI by using the following methods

  • webserver.content_send(content:string)
  • webserver.content_send_style(content:string)

Let's see an example implementation of button methods in a Driver class

import webserver # import webserver class
+
+class MyButtonMethods
+
+  def myOtherFunction(myValue)
+    #- do something -#
+  end
+
+  #- create a method for adding a button to the main menu -#
+  def web_add_main_button()
+    webserver.content_send("<p></p><button onclick='la(\"&m_toggle_main=1\");'>Toggle Main</button>")
+  end
+
+  #- create a method for adding a button to the configuration menu-#
+  def web_add_config_button()
+    #- the onclick function "la" takes the function name and the respective value you want to send as an argument -#
+    webserver.content_send("<p></p><button onclick='la(\"&m_toggle_conf=1\");'>Toggle Conf</button>")
+  end
+
+  #- As we can add only one sensor method we will have to combine them besides all other sensor readings in one method -#
+  def web_sensor()
+
+    if webserver.has_arg("m_toggle_main")
+      print("button pressed")
+    end
+
+    if webserver.has_arg("m_toggle_conf") # takes a string as argument name and returns a boolean
+
+      # we can even call another function and use the value as a parameter
+      var myValue = int(webserver.arg("m_toggle_conf")) # takes a string or integer(index of arguments) to get the value of the argument
+      self.myOtherFunction(myValue)
+    end
+
+    end
+  end
+d1 = MyButtonMethods()
+tasmota.add_driver(d1)
+

Creating an I2C driver~

Berry Scripting provides all necessary primitives for a complete I2C driver.

Step by step approach~

We will explore the different steps to write an I2C driver, and will take the MPU6886 as an example. The native driver already exists, and we'll rewrite it in Berry code.

Step 1: detect the device

I2C device are identified by address, only one device per address is allowed per I2C physical bus. Tasmota32 supports up to 2 I2C buses, using wire1 or wire2 objects.

To simplify device detection, we provide the convenience method tasmota.scan_wire(). The first argument is the device address (0x68 for MPU6886). The optional second argument is the I2C Tasmota index, allowing to selectively disable some device families. See I2CDevice command and page XXX. The index number for MPU6886 is 58.

class MPU6886
+  var wire     # contains the wire object if the device was detected
+
+  def init()
+    self.wire = tasmota.wire_scan(0x68, 58)
+  end
+end
+

self.wire contains a reference to wire1 if the device was detected on I2C bus 1, a reference to wire2 if the device was detected on bus 2, or nil if the device was not detected, or if I2C index 58 was disabled through I2CEnable.

Step 2: verify the device

To make sure the device is actually an MPU6886, we check its signature by reading register 0x75. It should respond 0x19 (see datasheet for MPU6886).

[...]
+    if self.wire
+      var v = self.wire.read(0x68,0x75,1)
+      if v != 0x19 return end  #- wrong device -#
+[...]
+

Step 3: initialize the device

We write a series of values in registers to configure the device as expected (see datasheet).

[...]
+      self.wire.write(0x68, 0x6B, 0, 1)
+      tasmota.delay(10)
+      self.wire.write(0x68, 0x6B, 1<<7, 1)    # MPU6886_PWR_MGMT_1
+      tasmota.delay(10)
+      self.wire.write(0x68, 0x6B, 1<<0, 1)    # MPU6886_PWR_MGMT_1
+      tasmota.delay(10)
+      self.wire.write(0x68, 0x1C, 0x10, 1)    # MPU6886_ACCEL_CONFIG - AFS_8G
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x1B, 0x18, 1)    # MPU6886_GYRO_CONFIG - GFS_2000DPS
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x1A, 0x01, 1)    # MPU6886_CONFIG
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x19, 0x05, 1)    # MPU6886_SMPLRT_DIV
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x38, 0x00, 1)    # MPU6886_INT_ENABLE
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x1D, 0x00, 1)    # MPU6886_ACCEL_CONFIG2
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x6A, 0x00, 1)    # MPU6886_USER_CTRL
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x23, 0x00, 1)    # MPU6886_FIFO_EN
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x37, 0x22, 1)    # MPU6886_INT_PIN_CFG
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x38, 0x01, 1)    # MPU6886_INT_ENABLE
+      tasmota.delay(100)
+[...]
+

We also pre-compute multiplier to convert raw values to actual values:

[...]
+      self.gres = 2000.0/32768.0
+      self.ares = 8.0/32678.0
+      print("I2C: MPU6886 detected on bus "+str(self.wire.bus))
+[...]
+

Step 4: read sensor value

We will detail here the acceleration senor; gyroscope works similarly and is not further detailed.

Reading the x/y/z sensor requires to read 6 bytes as a bytes() object

    var b = self.wire.read_bytes(0x68,0x3B,6)
+

Each value is 2 bytes. We use bytes.get(offset,size) to extract 2-bytes values at offsets 0/2/4. The size is -2 to indicate that values are encoded in Big Endian instead of Little Endian.

    var a1 = b.get(0,-2)
+

Finally the read value is unsigned 16 bits, but the sensor value is signed 16 bits. We convert 16 bits unsigned to 16 bits signed.

    if a1 >= 0x8000 a1 -= 0x10000 end
+

We then repeat for y and z:

  def read_accel()
+    if !self.wire return nil end  #- exit if not initialized -#
+    var b = self.wire.read_bytes(0x68,0x3B,6)
+    var a1 = b.get(0,-2)
+    if a1 >= 0x8000 a1 -= 0x10000 end
+    var a2 = b.get(2,-2)
+    if a2 >= 0x8000 a2 -= 0x10000 end
+    var a3 = b.get(4,-2)
+    if a3 >= 0x8000 a3 -= 0x10000 end
+    self.accel = [a1 * self.ares, a2 * self.ares, a3 * self.ares]
+    return self.accel
+  end
+

Step 5: read sensor every second

Simply override every_second()

  def every_second()
+    if !self.wire return nil end  #- exit if not initialized -#
+    self.read_accel()
+    self.read_gyro()
+  end
+

Step 6: display sensor value in Web UI

You need to override web_sensor() and provide the formatted string. tasmota.web_send_decimal() sends a string to the Web UI, and converts decimal numbers according to the locale settings.

Tasmota uses specific markers:

  • {s}: start of line
  • {m}: separator between name and value
  • {e}: end of line
  #- display sensor value in the web UI -#
+  def web_sensor()
+    if !self.wire return nil end  #- exit if not initialized -#
+    import string
+    var msg = string.format(
+             "{s}MPU6886 acc_x{m}%.3f G{e}"..
+             "{s}MPU6886 acc_y{m}%.3f G{e}"..
+             "{s}MPU6886 acc_z{m}%.3f G{e}"..
+             "{s}MPU6886 gyr_x{m}%i dps{e}"..
+             "{s}MPU6886 gyr_y{m}%i dps{e}"..
+             "{s}MPU6886 gyr_z{m}%i dps{e}",
+              self.accel[0], self.accel[1], self.accel[2], self.gyro[0], self.gyro[1], self.gyro[2])
+    tasmota.web_send_decimal(msg)
+  end
+

Step 7: publish JSON TelePeriod sensor value

Similarly to Web UI, publish sensor value as JSON.

  #- add sensor value to teleperiod -#
+  def json_append()
+    if !self.wire return nil end  #- exit if not initialized -#
+    import string
+    var ax = int(self.accel[0] * 1000)
+    var ay = int(self.accel[1] * 1000)
+    var az = int(self.accel[2] * 1000)
+    var msg = string.format(",\"MPU6886\":{\"AX\":%i,\"AY\":%i,\"AZ\":%i,\"GX\":%i,\"GY\":%i,\"GZ\":%i}",
+              ax, ay, az, self.gyro[0], self.gyro[1], self.gyro[2])
+    tasmota.response_append(msg)
+  end
+

Full example~

The code can be loaded manually with copy/paste, or stored in flash and loaded at startup in autoexec.be as load("mpu6886.be"). Alternatively it can be loaded with a Tasmota native command or rule:

Br load("mpu6886.be")
+

See code example below for MPU6886:

#-
+ - Example of I2C driver written in Berry
+ -
+ - Support for MPU6886 device found in M5Stack
+ - Alternative to xsns_85_mpu6886.ino 
+ -#
+
+class MPU6886
+  var wire          #- if wire == nil then the module is not initialized -#
+  var gres, ares
+  var accel, gyro
+
+  def init()
+    self.wire = tasmota.wire_scan(0x68, 58)
+
+    if self.wire
+      var v = self.wire.read(0x68,0x75,1)
+      if v != 0x19 return end  #- wrong device -#
+
+      self.wire.write(0x68, 0x6B, 0, 1)
+      tasmota.delay(10)
+      self.wire.write(0x68, 0x6B, 1<<7, 1)    # MPU6886_PWR_MGMT_1
+      tasmota.delay(10)
+      self.wire.write(0x68, 0x6B, 1<<0, 1)    # MPU6886_PWR_MGMT_1
+      tasmota.delay(10)
+      self.wire.write(0x68, 0x1C, 0x10, 1)    # MPU6886_ACCEL_CONFIG - AFS_8G
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x1B, 0x18, 1)    # MPU6886_GYRO_CONFIG - GFS_2000DPS
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x1A, 0x01, 1)    # MPU6886_CONFIG
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x19, 0x05, 1)    # MPU6886_SMPLRT_DIV
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x38, 0x00, 1)    # MPU6886_INT_ENABLE
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x1D, 0x00, 1)    # MPU6886_ACCEL_CONFIG2
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x6A, 0x00, 1)    # MPU6886_USER_CTRL
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x23, 0x00, 1)    # MPU6886_FIFO_EN
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x37, 0x22, 1)    # MPU6886_INT_PIN_CFG
+      tasmota.delay(1)
+      self.wire.write(0x68, 0x38, 0x01, 1)    # MPU6886_INT_ENABLE
+      tasmota.delay(100)
+
+      self.gres = 2000.0/32768.0
+      self.ares = 8.0/32678.0
+      print("I2C: MPU6886 detected on bus "+str(self.wire.bus))
+    end
+  end
+
+  #- returns a list of 3 axis, float as g acceleration -#
+  def read_accel()
+    if !self.wire return nil end  #- exit if not initialized -#
+    var b = self.wire.read_bytes(0x68,0x3B,6)
+    var a1 = b.get(0,-2)
+    if a1 >= 0x8000 a1 -= 0x10000 end
+    var a2 = b.get(2,-2)
+    if a2 >= 0x8000 a2 -= 0x10000 end
+    var a3 = b.get(4,-2)
+    if a3 >= 0x8000 a3 -= 0x10000 end
+    self.accel = [a1 * self.ares, a2 * self.ares, a3 * self.ares]
+    return self.accel
+  end
+
+  #- returns a list of 3 gyroscopes, int as dps (degree per second)  -#
+  def read_gyro()
+    if !self.wire return nil end  #- exit if not initialized -#
+    var b = self.wire.read_bytes(0x68,0x43,6)
+    var g1 = b.get(0,-2)
+    if g1 >= 0x8000 g1 -= 0x10000 end
+    var g2 = b.get(2,-2)
+    if g2 >= 0x8000 g2 -= 0x10000 end
+    var g3 = b.get(4,-2)
+    if g3 >= 0x8000 g3 -= 0x10000 end
+    self.gyro = [int(g1 * self.gres), int(g2 * self.gres), int(g3 * self.gres)]
+    return self.gyro
+  end
+
+  #- trigger a read every second -#
+  def every_second()
+    if !self.wire return nil end  #- exit if not initialized -#
+    self.read_accel()
+    self.read_gyro()
+  end
+
+  #- display sensor value in the web UI -#
+  def web_sensor()
+    if !self.wire return nil end  #- exit if not initialized -#
+    import string
+    var msg = string.format(
+             "{s}MPU6886 acc_x{m}%.3f G{e}"..
+             "{s}MPU6886 acc_y{m}%.3f G{e}"..
+             "{s}MPU6886 acc_z{m}%.3f G{e}"..
+             "{s}MPU6886 gyr_x{m}%i dps{e}"..
+             "{s}MPU6886 gyr_y{m}%i dps{e}"..
+             "{s}MPU6886 gyr_z{m}%i dps{e}",
+              self.accel[0], self.accel[1], self.accel[2], self.gyro[0], self.gyro[1], self.gyro[2])
+    tasmota.web_send_decimal(msg)
+  end
+
+  #- add sensor value to teleperiod -#
+  def json_append()
+    if !self.wire return nil end  #- exit if not initialized -#
+    import string
+    var ax = int(self.accel[0] * 1000)
+    var ay = int(self.accel[1] * 1000)
+    var az = int(self.accel[2] * 1000)
+    var msg = string.format(",\"MPU6886\":{\"AX\":%i,\"AY\":%i,\"AZ\":%i,\"GX\":%i,\"GY\":%i,\"GZ\":%i}",
+              ax, ay, az, self.gyro[0], self.gyro[1], self.gyro[2])
+    tasmota.response_append(msg)
+  end
+
+end
+mpu6886 = MPU6886()
+tasmota.add_driver(mpu6886)
+

LVGL Touchscreen with 3 Relays~

#- start LVGL and init environment -#
+lv.start()
+
+hres = lv.get_hor_res()     # should be 240
+vres = lv.get_ver_res()     # should be 320
+
+scr = lv.scr_act()          # default screean object
+f20 = lv.montserrat_font(20)  # load embedded Montserrat 20
+f28 = lv.montserrat_font(28)  # load embedded Montserrat 28
+
+#- Backgroun -#
+scr.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x000066))  # backgroun in dark blue #000066
+
+#- Upper state line -#
+stat_line = lv_label(scr)
+if f20 != nil stat_line.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f20) end
+stat_line.set_long_mode(lv.LABEL_LONG_SROLL)                                                  # auto scrolling if text does not fit
+stat_line.set_width(hres)
+stat_line.set_align(lv.LABEL_ALIGN_LEFT)                                                      # align text left
+stat_line.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x000088))    # background #000088
+stat_line.set_style_local_bg_opa(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv.OPA_COVER)            # 100% background opacity
+stat_line.set_style_local_text_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xFFFFFF))  # text color #FFFFFF
+stat_line.set_text("Tasmota")
+stat_line_height = stat_line.get_height()
+
+#- display wifi strength indicator icon (for professionals ;) -#
+stat_line.set_style_local_pad_right(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, stat_line_height + 1)
+wifi_bars = lv_wifi_bars(stat_line)
+wifi_bars.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(lv.BLACK))
+wifi_bars.set_height(stat_line_height)
+wifi_bars.set_width(stat_line_height)
+wifi_bars.set_x(stat_line.get_width() - stat_line_height)
+
+#- create a style for the buttons -#
+btn_style = lv_style()
+btn_style.set_radius(lv.STATE_DEFAULT, 10)                                                    # radius of rounded corners
+btn_style.set_bg_opa(lv.STATE_DEFAULT, lv.OPA_COVER)                                          # 100% background opacity
+if f28 != nil btn_style.set_text_font(lv.STATE_DEFAULT, f28) end
+btn_style.set_bg_color(lv.STATE_DEFAULT, lv_color(0x33BBFF))                                  # background color #1FA3EC (Tasmota Blue)
+btn_style.set_border_color(lv.STATE_DEFAULT, lv_color(0x0000FF))                              # border color #0000FF
+#btn_style.set_bg_color(lv.STATE_FOCUSED, lv_color(0x0000FF))                                  # background color when pressed #0000FF
+#btn_style.set_border_color(lv.STATE_FOCUSED, lv_color(0xFFFFFF))                              # border color when pressed #FFFFFF
+btn_style.set_text_color(lv.STATE_DEFAULT, lv_color(0x000000))                                # text color #FFFFFF
+#- enabled -#
+btn_style.set_bg_color(lv.STATE_CHECKED, lv_color(0x0000FF))                                  # background color #1FA3EC (Tasmota Blue)
+btn_style.set_text_color(lv.STATE_CHECKED, lv_color(0xFFFFFF))                                # text color #FFFFFF
+btn_style.set_outline_width(lv.STATE_FOCUSED, 0)                                              # remove focus outline, not needed with touchscreen
+
+#- register buttons -#
+var btns = []         # relay buttons are added to this list to match with Tasmota relays
+
+#- simple function to find the index of an element in a list -#
+def findinlist(l, x)
+  for i:0..size(l)-1
+    if l[i] == x
+      return i
+    end
+  end
+end
+
+#- callback function when a button is pressed -#
+#- checks if the button is in the list, and react to EVENT_VALUE_CHANGED event -#
+def btn_event_cb(o, event)
+  var btn_idx = findinlist(btns, o)
+  if btn_idx != nil && event == lv.EVENT_VALUE_CHANGED
+    var val = o.get_state() < lv.BTN_STATE_CHECKED_RELEASED   # true if checked, false if unchecked
+    tasmota.set_power(btn_idx, !val)                          # toggle the value
+  end
+end
+
+#- create a button object, set style, register callback and add to global list -#
+#- you still need to re-position the button -#
+def create_btn_relay(label)
+  var btn, btn_label
+  btn = lv_btn(scr)
+  btn.set_pos(30, 30)
+  btn.set_size(hres - 60, 60)
+  btn.add_style(lv.OBJ_PART_MAIN, btn_style)
+  btn.set_checkable(true)                                                                      # enable toggle mode
+  btn_label = lv_label(btn)
+  btn_label.set_text(label)
+  btn.set_event_cb(btn_event_cb)                            # set callback to update Tasmota relays
+  btns.push(btn)                                            # append button to the list
+  return btn
+end
+
+#- create 3 buttons -#
+var btn1 = create_btn_relay("Relay 1")
+btn1.set_y(30)
+var btn2 = create_btn_relay("Relay 2")
+btn2.set_y(100)
+var btn3 = create_btn_relay("Relay 3")
+btn3.set_y(170)
+
+#- update the buttons values according to internal relays status -#
+def btns_update()
+  var power_list = tasmota.get_power()                                            # get a list of booleans with status of each relay
+  for b:btns
+    var state = b.get_state()
+    var power_state = (size(power_list) > 0) ? power_list.pop(0) : false          # avoid exception if less relays than buttons
+    if state != lv.BTN_STATE_PRESSED && state != lv.BTN_STATE_CHECKED_PRESSED     # update only if the button is not currently being pressed
+      b.set_state(power_state ? lv.BTN_STATE_CHECKED_RELEASED : lv.BTN_STATE_RELEASED)
+    end
+  end
+end
+
+#- update every 500ms -#
+def btns_update_loop()
+  btns_update()
+  tasmota.set_timer(500, btns_update_loop)
+end
+btns_update_loop()  # start
+
+# If you change the style after creating the button, you need to update objects:
+def btns_refresh_style()
+  for b:btns b.refresh_style(lv.OBJ_PART_MAIN, lv.STYLE_PROP_ALL) end
+end
+
+# Button states, read and set with:
+#   btn1.get_state()  or  btn1.set_state(lv.BTN_STATE_CHECKED_RELEASED)
+# Ex:
+#    btn1.set_state(lv.BTN_STATE_RELEASED)
+#    btn1.set_state(lv.BTN_STATE_CHECKED_RELEASED)
+
+#- Here are the states for buttons -#
+# BTN_STATE_RELEASED
+# BTN_STATE_PRESSED
+# BTN_STATE_DISABLED
+# BTN_STATE_CHECKED_RELEASED
+# BTN_STATE_CHECKED_PRESSED
+# BTN_STATE_CHECKED_DISABLED
+

Multi-Zone Heating Controller~

This project is a multi-zone heating controller written entirely in berry. It demonstrates the use of the persist module for saving/loading data; the webserver module for creating a custom "Manage Heating" user interface; dynamic loading of HTML from the file system; subscribing to a variety of rule triggers (using tasmota.add_rule); the implementation of custom commands (using tasmota.add_cmnd). It also makes good use of time functionaity (via tasmota.rtc, tasmota.time_dump, tasmota.set_timer and tasmota.strftime). The project also includes an LCD I2C driver for running a basic 20x4 display. The entire driver is implemented using just the tasmota.wire_scan method.

https://github.com/Beormund/Tasmota32-Multi-Zone-Heating-Controller

Ethernet Network Flipper~

Used on board with Ethernet. If both Wi-Fi and Ethernet are active, turn off Wi-Fi. Place code in autoexec.be to execute on boot. You can call the function from Berry console any time with netflip().

def netflip()
+  var eth = tasmota.eth().find('ip') != nil   #1
+  if tasmota.wifi().find('ip') != nil == eth  #2
+    tasmota.cmd('Wifi ' .. (eth ? 0 : 1))     #3
+  end
+end
+tasmota.set_timer(30000,netflip)              #4
+
  1. store variable "eth" with Ethernet status - "true" if Ethernet IP exists and "false" if not
  2. check if wifi status is true and compare to eth status
  3. send command Wifi with parameter depending on eth variable. .. is to concatenate a string. See Berry manual
  4. set a timer to execute the netflip function 30000ms (30 seconds) after loading autoexec.be

TMP117 Driver~

TomsTek@GitHub

Call function at intervals~

This small helper function allows you to call a function at stable intervals, automatically correcting in case of latency or other deviations. Not suitable for very short intervals; while the delay interval is in milliseconds for consistency with the standard tasmota.set_timer, it would normally be seconds multiplied by 1000, like 60000 for every minute.

def set_timer_modulo(delay,f,id)
+  var now=tasmota.millis()
+  tasmota.set_timer((now+delay/4+delay)/delay*delay-now, def() set_timer_modulo(delay,f,id) f() end, id)
+end
+

H-bridge control~

An H-bridge is an electronic circuit that switches the polarity of a voltage applied to a load. These circuits are often used in robotics and other applications to allow DC motors to run forwards or backwards.

You can typically use 2 PWM channels to pilot a H-bridge, under the condition that both channels are never active at the same time; otherwise you may destroy your device. This means that phasing must be calculated so that one pulse started once the other pulse is inactive, and the sum of both dutys must not exceed 100%.

The following Berry function ensures appropriate management of H-bridge:

#
+# H_bridge class in Berry to pilot a H-bridge device
+#
+
+class H_bridge
+  var gpio1, gpio2
+  var max
+
+  # init(phy_gpio1, phy_gpio2) - initialize H-bridge with the 2 GPIOs used to control it
+  def init(gpio1, gpio2)
+    self.gpio1 = gpio1
+    self.gpio2 = gpio2
+    self.max = 1023     # max value of duty
+  end
+
+  # set the value of both PWM values
+  def set(v1, v2)
+    if v1 < 0   v1 = 0 end
+    if v2 < 0   v2 = 0 end
+    if v1 + v2 > self.max
+      raise "value_error", "the sum of duties must not exceed 100%"
+    end
+
+    import gpio
+    gpio.set_pwm(self.gpio1, v1, 0)
+    gpio.set_pwm(self.gpio2, v2, v1)    # dephase by value v1
+  end
+end
+

Example of use:

var hbridge = H_bridge(12, 13)    # use GPIO12 and GPIO13
+hbridge.set(100,200)              # set values to 102/1023 and 204/1023, i.e. 10% and 20%
+
+hbridge.set(100,950)              # set values to 102/1023 and 950/1023, i.e. 10% and 93%
+BRY: Exception> 'value_error' - the sum of duties must not exceed 100%
+

Flash to file~

This is an example of dumping the content of the internal flash of the ESP32 and write the content in the file system that you can download back to your PC.

The example below dumps the contant of the safeboot partition.

def flash_to_file(filename, addr, len)
+  import flash
+  import string
+
+  var f = open(filename, "wb")
+  try
+    # Do 4KB chunks
+
+    while len > 0
+      var chunk = 512
+      if len < chunk    chunk = len end
+      var b = flash.read(addr, chunk)
+      print(string.format("0x%06X - %s (%i)", addr, str(b), chunk))
+      f.write(b)
+      b = nil
+
+      addr += chunk
+      len -= chunk
+    end
+
+    f.close()
+  except .. as e,m
+    f.close()
+  end
+
+end
+
+flash_to_file("safe_boot_flashed.bin", 0x10000, 768320)
+

Tank Sensor~

Tank Sensor for fuel-oil volume measurement using a VL53L1X or SR04 sensor

https://github.com/trlafleur/Tasmota-Tank-Sensor

Home Assistant Controls for Tasmota~

This is a Tasmota Berry Script library to greatly simplify the process of exposing Home Assistant controls (e.g. Pull-down Lists, Number Sliders, Sensors, etc.) from a Tasmota device - and handling the communication between both sides.

https://github.com/fmtr/hct

Build MQTT topic string based on FullTopic configuration~

This code bit illustrates how you can create a topic string in the form of the FullTopic specification. Details like whech %prefix% you want and what last level of the topic is obviously variable.

var topic = string.replace(string.replace(
+              tasmota.cmd('FullTopic',true)['FullTopic'],
+              '%topic%', tasmota.cmd('Topic',true)['Topic']),
+              '%prefix%', tasmota.cmd('Prefix',true)['Prefix3'])
+            + 'SENSOR'
+
\ No newline at end of file diff --git a/Berry-Introduction/index.html b/Berry-Introduction/index.html new file mode 100644 index 0000000000..aac0e80da5 --- /dev/null +++ b/Berry-Introduction/index.html @@ -0,0 +1,304 @@ + Berry Introduction (in 20 minutes or less) - Tasmota
Skip to content

Berry Introduction (in 20 minutes or less)~

This quick start will drive you in the basics of the Berry language. It should take no more than 20 minutes and is inspired by Ruby in Twenty Minutes

Berry is an ultra-lightweight dynamically typed scripting language. It is designed for lower-performance embedded devices. It also runs on a regular computer, and it can run directly in your browser for quick testing.

Berry is the next generation scripting for Tasmota, embedded by default in all ESP32 based firmwares. It is used for advanced scripting and superseded Rules. Its advanced features are used to extend Tasmota: adding commands, adding drivers (I2C, serial...), extending the web UI, adding full applications (TAPP files), driving advanced graphics with LVGL.

To start with Berry, you have at least 3 choices:

  • use the Berry online console and start in less than 10 seconds
  • flash an ESP32 based device with Tasmota and use the Berry console
  • compile Berry on your computer from sources and run the Berry interpreter (less preferred)

Hello, Berry~

In the console type:

> print("Hello, Berry")
+Hello, Berry
+

What just happened? We just sent the simplest possible Berry program print("Hello, Berry"). Internally this program was compiled into Berry bytecode and ran using the Berry virtual machine.

In Berry you can append commands one after the other. Contrary to C you don't need any separator like ;. Unlike Python indentation has no importance. Commands need only to be separated by at least one space-like character: space, tab, newline.

> print("Hello, Berry") print("Hello, Berry")
+Hello, Berry
+Hello, Berry
+

In this second example, the implicit program contains 2 commands.

Your free calculator is here~

Not surprisingly, like most scripting languages you can do direct calculation.

> 3+2
+5
+> 3*2
+6
+

The above computations are made against integers. Berry supports either 32 bits or 64 bits integers depending on the underlying platform (usually 32 bits on embedded systems).

Berry supports floating point calculation, as soon as at lest one member if floating point. Floating point uses either 32 bits float or 64 bits double depending on compilation options (usually 32 bits on embedded systems).

> 3/2
+1
+> 3.0/2
+1.5
+> 1/3.0
+0.333333
+

The command 3/2 works on integers and returns an integer result. 3.0/2, 3/2.0 or 3.0/2.0 work on floating point numbers since at least one operand is floating point.

You can convert an integer to floating point using real() and truncate to integer with int().

> 3/2
+1
+> real(3)/2
+1.5
+> int(3.0/2)
+1
+

Beyond the core Berry language, advanced math function are available via the additional module math see documentation.

> import math
+> math.sqrt(2)       # square root of 2
+1.41421
+
+> math.pow(2,3)      # 2^3
+8
+

Defining a function~

What if you want to say "Hello" a lot without getting your fingers all tired? You should define another function:

> def hi() print("Hello, Berry") end
+> 
+

Now let's call the function:

> hi()
+Hello, Berry
+

hi is a function that takes no argument, returns nothing, and prints a message in the console. Calling a function always requires sending arguments between parenthesis (). Otherwise Berry thinks that you want to manipulate the function itself as an entity.

> hi           # return the function entity itself
+<function: 0x3ffdac6c>
+
+> hi()         # call the function
+Hello, Berry
+

What if we want to say hello to one person, and not only to Berry? Just redefine hi function to take a name as an argument.

> def hi(name) print("Hello, " + name) end
+

This way, hi is a function that takes a single argument as string.

> hi("Skiars") hi("Theo")
+Hello, Skiars
+Hello, Theo
+

This function only works if the argument is a string, and fails if you use any other type of argument. Let's use str() built-in function to force-convert the argument to a string.

> def hi(name) print("Hello, " + str(name)) end
+
+> hi("Skiars") hi("Theo")
+Hello, Skiars
+Hello, Theo
+
+> hi(2)
+Hello, 2
+

What happens if you don't send any argument to a function that expects one? Let's try:

> def hi(name) print("Hello, " + str(name)) end
+
+> hi()
+Hello, nil
+

The knights who say nil~

What is this nil thing? Berry has a special value nil meaning "nothing". nil is the implicit value passed to a function when no argument is sent, or the value returned by a function that does not return anything.

> nil
+nil
+
+> hi(nil)
+Hello, nil
+
+> hi()
+Hello, nil
+

As you see, nil is the implicit value passed when arguments are missing, but also a value that you can pass explicitly.

Formatting strings~

In the above example, we only concatenated two strings. Berry provides a more advanced scheme to format numerical values as well. It is widely inspired from C formatting used by printf. Don't forget to import the string module first.

> import string
+> def say_hi(name) print(string.format("Hello, %s!", name)) end
+> def say_bye(name) print(string.format("Bye, %s, come back soon", name)) end
+
+> say_hi("Bob")
+Hello, Bob!
+> say_bye("Bob")
+Bye, Bob, come back soon
+

You can combine with string functions like toupper() to convert to uppercase

> import string
+> name = "Bob"
+> string.format("Hello, %s!", string.toupper(name))
+Hello, BOB!
+

In the example above, we have created a global variable called name containing the string "Bob" and used string.toupper() to convert it to all uppercase.

Evolving into a Greeter~

What if we want a real greeter around, one that remembers your name and welcomes you and treats you always with respect. You might want to use an object for that. Let’s create a “Greeter” class.

Note: since it's a multi-line example, you may need to copy the entire block and paste it at once in the console (not line-by-line).

class Greeter
+  var name
+
+  def init(name)
+    self.name = name
+  end
+
+  def say_hi()
+    import string
+    print(string.format("Hi %s", self.name))
+  end
+
+  def say_bye()
+    import string
+    print(string.format("Bye %s, come back soon.", self.name))
+  end
+end
+

The new keyword here is class. This defines a new class called Greeter and a bunch of methods for that class. Also notice var name. This is an instance variable, and is available to all the methods of the class. As you can see it’s used by say_hi and say_bye as self.name.

The init() method is a special method called a "constructor". It is implicitly called when you create a new instance for the class, and the arguments are passed to init(). The constructor is responsible for complete initialization of the object, and it's always the first method called. The above example is typical of any object: it takes an argument name and copies it to an instance variable self.name to make it available to any method.

Note: Berry has no concept of private members (contrary to C++). All instance variables and methods are always public.

Creating a greeter object~

Now let’s create a greeter object and use it:

> greeter = Greeter("Pat")
+> greeter.say_hi()
+Hi Pat
+> greeter.say_bye()
+Bye Pat, come back soon.
+

Once the greeter object is created, it remembers that the name is Pat. If you want to get the name from a greeter, you can ask a greeter by accessing the name variable on it (without parenthesis):

> greeter.name
+Pat
+

Subclasses~

Methods and instance variables are defined at the class creation. You can't add method or instance variables to an already existing class. To extend a class you can create a sub-class:

class SurGreeter : Greeter     # subclass of Greeter
+  var surname
+
+  def init(name, surname)      # sub-class takes 2 arguments
+    super(self).init(name)     # call constructor of super-class
+    self.surname = surname
+  end
+
+  def say_hi()
+    import string
+    print(string.format("Hi %s %s", self.name, self.surname))
+  end
+end
+

The class SurGreeter extends Greeter with an additional surname field. It overwrides say_hi() but leaves say_bye() unchanged.

There is a special syntax for calling a method of the subclass super(self).init(name).

Note: classes have always an init() method, either because it was explicitly defined, or implicitly. It is always ok to call super(self).init() even if the subclass has no explicit init() method.

Now let's try this new class:

> greet = SurGreeter("John", "Smith")
+> greet.say_hi()
+Hi John Smith
+> greet.say_bye()
+Bye John, come back soon.
+
+> greet.name
+John
+> greet.surname
+Smith
+

Greetings everyone!~

This greeter isn’t all that interesting though, it can only deal with one person at a time. What if we had some kind of MegaGreeter that could either greet the world, one person, or a whole list of people? Let’s try to build that. We will start with a class definition:

class MegaGreeter
+  var names
+
+  def init(name)
+    self.names = []          # empty list
+    if name != nil
+      self.names.push(name)
+    end
+  end
+end
+

So MegaGreeter objects have a list of names. The names field is initialized to the empty list []. The body of the MegaGreeter constructor adds the given name argument to the end of the list of names if it's not nil. Mega greeters don't have a single name and no name field, so here the name is just an ordinary parameter that we can use in the body of the constructor.

Let's try it:

> greeter = MegaGreeter()
+> greeter.names
+[]
+
+> greeter = MegaGreeter("World")
+> greeter.names
+['World']
+

We can now go ahead and add greeter methods that add more names and show all the names:

class MegaGreeter
+  var names
+
+  def init(name)
+    self.names = []          # empty list
+    if name != nil
+      self.names.push(name)
+    end
+  end
+
+  def add(name)
+    self.names.push(name)
+  end
+
+  def say_hi()
+    import string
+    for n: self.names
+      print(string.format("Hello %s!", n))
+    end
+  end
+
+  def say_bye()
+    import string
+    for n: self.names
+      print(string.format("Bye %s, come back soon.", n))
+    end
+  end
+end
+

We introduced here a new construct known as an iterator. for n: self.names creates a new local variable n and iterate the following code for each value in self.names.

Let's try the full example now:

> greeter = MegaGreeter()
+> greeter.add("Skiars")
+> greeter.add("Theo")
+> greeter.add("Stephan")
+
+> greeter.say_hi()
+Hello Skiars!
+Hello Theo!
+Hello Stephan!
+
+> greeter.say_bye()
+Bye Skiars, come back soon.
+Bye Theo, come back soon.
+Bye Stephan, come back soon.
+

Comments~

Sometimes, it is nice just to add comments that explain interesting things related to your code. In the example in the last section, there were a few single line comments:

  self.names = []          # empty list
+

Such comments start with # and tell the system to ignore the rest of the line.

You can also use multi-line comments starting with #- and ending with -#.

#-
+ This is a comment
+-#
+
+#-
+# This is also a comment block (`#` are ignored)
+-#
+
+#-----------------------------------------
+ Alternative way to make comment blocks
+ -----------------------------------------#
+

Indentation has no impact on Berry compiler, it's only by convention to make source code more readable.

Maps~

Maps are a very common and powerful feature to store key/value pairs. They are declared using {}.

> m1 = {}           # empty map
+> m
+{}
+
+> m2 = {"k1":"v1", "k2":"v2", "k3":"v3"}
+> m2
+{'k2': 'v2', 'k1': 'v1', 'k3': 'v3'}
+

Actually keys and values can be of arbitrary type.

> m3 = { 1.5: 3, 2:"two", true:1, false:nil }
+> m3
+{1.5: 3, true: 1, 2: 'two', false: nil}
+

The main restriction is that a key can't be nil. Setting adding a key of value nil is silently ignored.

> m4 = { nil:"foo" }
+> m4
+{}
+

Accessing a value in the map uses [<key>]:

> m1 = {}
+> m1['k1'] = "value1"
+> m1
+{'k1': 'value1'}
+
+# working with numerical values
+> m1['k2'] = 0
+> m1['k2'] += 5      # shortcut for `m1['k2'] = m1['k2'] + 5`
+> m1
+{'k': 'value', 'k2': 5}
+

Accessing a non-existent key raises an error. There is an alternative function find() to access a key and return a default value if the key is absent. contains() can also be used to check the presence of the key.

> m1 = {"foo":"bar"}
+> m1.contains("foo")
+true
+> m1.contains("bar")      # only checks for keys, not values
+false
+
+> m1["foo"]
+bar
+> m1["bar"]
+key_error: bar
+stack traceback:
+   <native>: in native function
+   stdin:1: in function `main`
+
+# alternative with find
+> m1.find("foo", "not_found")
+bar
+> m1.find("bar", "not_found")
+not_found
+> m1.find("bar")          # returns nil by default if not found
+

Note: m[k] = v is syntactic sugar for m.setitem(k, v). When reading a value, m[k] is equivalent to m.item(k).

If statements and basic expressions~

We can program a ridiculously inefficient Fibonacci sequence generator using if and recursion:

def fib(n)
+  if n <= 1 return n end
+  return fib(n-1) + fib(n-2)
+end
+

This defines a top-level function called fib that is not a member of any class. The fib function is recursive, calling itself, and also makes use of a few new features. The if-statement is well known from other languages. In Berry it works by taking an expression and conditionally evaluating a block.

Berry also has the usual array of infix operators, +, -, *, /, % etc. and the relational operators <, <=, >, >=, == and !=.

> fib(10)
+55
+

Cycling and Looping~

As we've seen in MegaGreeter it is very simple to iterate over a list for n: self.names [...] end.

Iterators can also be used over ranges like for i:0..4 which will iterate over all values between 0 and 4 inclusive (5 iterations in total).

> for i:0..4 print(i) end
+0
+1
+2
+3
+4
+

Iterating over maps goes in two flavors: iterating over values, or over keys.

> m = {"k1":"v1", "k2":"v2", "k3":"v3"}
+> print(m)     # keep in mind that there is no order in a map
+{'k2': 'v2', 'k1': 'v1', 'k3': 'v3'}
+
+# iterate over values
+> for v: m          print(v) end
+v2
+v1
+v3
+
+# iterate over keys
+> for k: m.keys()   print(k) end
+k2
+k1
+k3
+
+# iterate over both keys and values
+> for k: m.keys()   print(k, m[k]) end
+k2 v2
+k1 v1
+k3 v3
+

For C programmers, the equivalent of for (int i=0; i<a; i++) { [...] } is for i: 0..a-1 [...] end

Functions and arguments~

In Berry, functions are first class entities (Berry supports functional programming as well as object oriented). Berry is not a strongly types language, which means that you don't define any type as input or output when you define a function. This may seem as a problem, but it's a very powerful feature instead.

Berry relies on what is known as "Duck Typing", as in “If it walks like a duck and it quacks like a duck, then it must be a duck”. As long as the type you provide supports the right methods and calls, then it's fine.

A function only defines the number of arguments it receives:

> def f(a, b) return str(a) + str(b) end        # takes only 2 arguments
+

f expects 2 arguments, if you provide less than 2, the non-defined are set to nil. If you provide more than 2, the extra-arguments are silently ignored.

> def f(a, b) return str(a) + str(b) end        # takes only 2 arguments
+> f("foo", "bar")
+foobar
+> f("foo")
+foonil
+> f("foo", "bar", "baz")
+foobar
+

A function may or may not return a value with return <expression>. If you call just return or the function ends without any return statement, the function returns nil.

Closures~

Let's finish this introduction with a very powerful feature known as closures. It is sometimes seen as intimidating or complex, but it's actually very simple. We will visit only the most common use of closures, if you want to get more details see the Berry documentation.

Let's go back to our simple Byer example (class that says Bye).

class Byer
+  var name
+  def init(name)
+    self.name = name
+  end
+  def say_bye()
+    import string
+    print(string.format("Bye %s, see you soon.", self.name))
+  end
+end
+

Let's define an instance of this class:

> bye_bob = Byer("Bob")
+> bye_pat = Byer("Pat")
+> bye_bob.say_bye()
+Bye Bob, see you soon.
+

Nothing new until now. Closure are useful as soon as you need callbacks. Let's say that you are using a framework that accepts a callback (a function you provide that will be fired in the future). We want to pass a function that says Bye to Bob.

The naive approach would be to use bye_bob.say_bye method, which is a valid function. However this function has no context and can't know which instance you are referring to.

> bye_bob.say_bye
+<function: 0x3ffb3200>
+> bye_pat.say_bye
+<function: 0x3ffb3200>    # same function as above
+

As shown above, since the context is missing, you can't distinguish from the method bye_bob.say_bye and bye_pat.say_bye. They are the same function.

Closure allows to create a new synthetic function that encapsulates transparently the context.

> cb = def () bye_bob.say_bye() end
+> cb
+<function: 0x3ffd9df4>
+
+# let's check that a closure on bye_pat is different
+> cb_pat = def () bye_pat.say_bye() end
+> cb_pat
+<function: 0x3ffdaaa0>
+

cb is a closure, if creates a function that captures the instance bye_bob and then calls say_bye() on it. Let's call the closures to check they are working.

> cb()
+Bye Bob, see you soon.
+

Tasmota this is widely used in Tasmota for example for deferred functions. For example if you want to run bye_bob.say_bye() in 5 seconds in the future:

> tasmota.set_timer(5000, cb)     # cb() is called in 5000 milliseconds
+

Advanced users: there is a compact syntax for simple callbacks: def cb(a,b) return <expr> end becomes/ a,b -> <expr>

Consider Yourself Introduced~

So that's a quick tour of Berry. Please have a look at the online Berry documentation.

For Tasmota users, also have a look at the Tasmota Berry documentation and Tasmota Berry Cookbook.

Extra~

Here is a short comparison of Berry and Python syntax, courtesy of @Beormund

Berry vs Python Berry MicroPython
Current object self self
Single line comments # #
Multi line comments #- ... -#
Logical 'and', 'or' and not operators && || ! and or not
Shift left, right << >> << >>
Integer division / //
Statement blocks/grouping (scope) (indent)
Class definition & inheritance class a:b class a(b):
Class constructor def init(x) ... end def __init__(self, x):
Class and superclass constructors def init(x)
super(self).init(x)
end
def __init__(self, x): super(b, self).__init__(x)
Class constructor that assigns to fields def init(x)
self.x = x
end
def __init__(self, x): self.x = x
Check object's type isinstance(b, a) isinstance(b, a)
Call method foo with 2 arguments foo(x, y) foo(x,y)
Declare a member variable in a class self.x = nil self.x = None
Declare a local variable in a method var x = 2, y = nil x = 2
y = None
Define a constant in a class static x = 2
Define a top level function def foo(x,y) end def foo(x,y):
Define an instance method in a class def foo(x,y) end def foo(self, x, y):
Define a static method in a class static def foo(x,y) end
If statement if condition end if condition:
Fixed loop for i: range end for i in range(end):
Iterate over collection for k: coll.keys() end for x in coll:
While loop while condition end while condition:
Import from library import library import library
Print print('hello world') print('Hello world')
Interpolation string.format("Hello %s", name) print("Hello %s" %(name))
Simple types int
real
bool (true\|false)
string
nil
int
float
bool (True\|False)
string
None
Class types list
map
range


list
dict

tuple
set
\ No newline at end of file diff --git a/Berry-Scripting/index.html b/Berry-Scripting/index.html new file mode 100644 index 0000000000..c8a247af83 --- /dev/null +++ b/Berry-Scripting/index.html @@ -0,0 +1,11 @@ + + + + + + + + +Redirecting... + + diff --git a/Berry/index.html b/Berry/index.html new file mode 100644 index 0000000000..20c1987654 --- /dev/null +++ b/Berry/index.html @@ -0,0 +1,723 @@ + Berry - Tasmota
Skip to content

Berry Scripting Language ~

Berry Scripting is included in all tasmota32 builds. It is NOT supported on ESP82xx

If you plan to code in Berry, you should enable #define USE_BERRY_DEBUG which will give you much more details when coding

Berry logo

Useful resources:

If you're new to Berry, have a look at Berry Introduction (in 20 minutes of less)

Introduction to Berry~

Berry is the next generation scripting for Tasmota. It is based on the open-source Berry project, delivering an ultra-lightweight dynamically typed scripting language designed for lower-performance embedded devices.

Github Manual

Reference sheet

Download Berry Short Manual to get a list of basic functions and capabilities of Berry language

Berry Scripting allows simple and also advanced extensions of Tasmota, for example:

  • simple scripting
  • advanced rules, beyond what is possible with native rules
  • advanced automations

Berry Scripting takes it one step further and allows to build dynamic extensions to Tasmota, that would previously require native code:

  • build light animations
  • build I2C drivers
  • build complete Tasmota drivers
  • integrate native libraries like lvgl see LVGL

About the Berry language~

Berry has the following advantages:

  • Lightweight: A well-optimized interpreter with very little resources. Ideal for use in microprocessors.
  • Fast: optimized one-pass bytecode compiler and register-based virtual machine.
  • Powerful: supports imperative programming, object-oriented programming, functional programming.
  • Flexible: Berry is a dynamic type script, and it's intended for embedding in applications. It can provide good dynamic scalability for the host system.
  • Simple: simple and natural MicroPython-eque syntax, supports garbage collection and easy to use FFI (foreign function interface).
  • RAM saving: With compile-time object construction, most of the constant objects are stored in read-only code data segments, so the RAM usage of the interpreter is very low when it starts.

Tasmota Port~

Berry Scripting in only supported on Tasmota32 for ESP32. The RAM usage starts at ~10kb and will be later optimized. Berry uses PSRAM on ESP32 if available (PSRAM is external RAM attached to ESP32 via SPI, it is slower but larger than internal RAM.

Quick Start~

Click on Configuration then Berry Scripting Console and enjoy the colorful Berry console, also called REPL (Read-Eval-Print-Loop).

Berry console

Drag the bottom corner of each screen to change its size

The console is not designed for big coding tasks but it's recommended to use a code editor when dealing with many, many lines of code. An extension for Visual Studio Code exists to make writing Berry scripts even easier with colored syntax. Download the entire folder and copy to VSCode extensions folder.

REPL Console~

Try typing simple commands in the REPL. Since the input can be multi-lines, press Enter twice or click "Run" button to run the code. Use Up and Down to navigate through history of previous commands.

> 1+1
+2
+
> 2.0/3
+0.666667
+
> print('Hello Tasmota!')
+Hello Tasmota!
+

Note: Berry's native print() command displays text in the Berry Console and in the Tasmota logs. To log with finer control, you can also use the log() function which will not display in the Berry Console.

> print('Hello Tasmota!')
+  log('Hello again')
+Hello Tasmota!
+

Meanwhile the Tasmota log shows:

> tasmota.cmd("Dimmer 60")
+{'POWER': 'ON', 'Dimmer': 60, 'Color': '996245', 'HSBColor': '21,55,60', 'Channel': [60, 38, 27]}
+The light is bright
+

Save your Scripts~

Berry can autostart your scripts. See a short desciption in the Section about the Filesystem: https://tasmota.github.io/docs/UFS/#autoexecbe Your can use the Filemanager to edit or save files with your berry scripts.

Iterate without rebooting~

Since v13.0.0.1 you can restart the entire Berry VM with a click in the Berry console. This feature requires to compile with #define USE_BERRY_DEBUG which is anyways highly recommended when coding in Berry. Be aware that restarting the Berry VM loses all context, and may generate negative side effects that we haven't yet identified. When restarting the VM, autoexec.be is ran again.

Instead of using the Web UI, you can also use the BrRestart command which does not require #define USE_BERRY_DEBUG.

Lights and Relays~

Berry provides complete support for Relays and Lights.

You can control individual Relays or lights with tasmota.get_power() and tasmota.set_power().

tasmota.get_power() returns an array of booleans representing the state of each relays and light (light comes last).

tasmota.set_power(relay, onoff) changes the state of a single relay/light.

2 relays and 1 light

> tasmota.get_power()
+[false, true, false]
+
+> tasmota.set_power(0, true)
+true
+
+> tasmota.get_power()
+[true, true, false]
+

For light control, light.get() and light.set accept a structured object containing the following arguments:

Attributes Details
power boolean
Turns the light off or on. Equivalent to tasmota.set_power(). When brightness is set to 0, power is automatically set to off. On the contrary, you need to specify power:true to turn the light on.
bri int range 0..255
Set the overall brightness. Be aware that the range is 0..255 and not 0..100 as Dimmer.
hue int 0..360
Set the color Hue in degree, range 0..360 (0=red).
sat int 0..255
Set the color Saturation (0 is grey).
ct int 153..500
Set the white color temperature in mired, ranging from 153 (cold white) to 500 (warm white)
rgb string 6 hex digits
Set the color as hex RRGGBB, changing color and brightness.
channels array of int, ranges 0..255
Set the value for each channel, as an array of numbers

When setting attributes, they are evaluated in the following order, the latter overriding the previous: power, ct, hue, sat, rgb, channels, bri.

  # set to yellow, 25% brightness
+> light.set({"power": true, "hue":60, "bri":64, "sat":255})
+{'bri': 64, 'hue': 60, 'power': true, 'sat': 255, 'rgb': '404000', 'channels': [64, 64, 0]}
+
+  # set to RGB 000080 (blue 50%)
+> light.set({"rgb": "000080"})
+{'bri': 128, 'hue': 240, 'power': true, 'sat': 255, 'rgb': '000080', 'channels': [0, 0, 128]}
+
+  # set bri to zero, also powers off
+> light.set({"bri": 0})
+{'bri': 0, 'hue': 240, 'power': false, 'sat': 255, 'rgb': '000000', 'channels': [0, 0, 0]}
+
+  # changing bri doesn't automatically power
+> light.set({"bri": 32, "power":true})
+{'bri': 32, 'hue': 240, 'power': true, 'sat': 255, 'rgb': '000020', 'channels': [0, 0, 32]}
+
+  # set channels as numbers (purple 12%)
+> light.set({"channels": [32,0,32]})
+{'bri': 32, 'hue': 300, 'power': true, 'sat': 255, 'rgb': '200020', 'channels': [32, 0, 32]}
+

Rules~

The rule function have the general form below where parameters are optional:

def function_name(value, trigger, msg)
+end
+
Parameter Description
value The value of the trigger. Similar to %value% in native rules.
trigger string of the trigger with all levels. Can be used if the same function is used with multiple triggers.
msg map Berry structured object of the message, decoded from JSON. If JSON was invalid, it contains the original string

Dimmer rule

Define the function and add a rule to Tasmota where the function runs if Dimmer value is more than 50

> def dimmer_over_50()
+    print("The light is bright")
+  end
+  tasmota.add_rule("Dimmer>50", dimmer_over_50)
+

> tasmota.cmd("Dimmer 30")
+{'POWER': 'ON', 'Dimmer': 30, 'Color': '4D3223', 'HSBColor': '21,55,30', 'Channel': [30, 20, 14]}
+
+> tasmota.cmd("Dimmer 60")
+{'POWER': 'ON', 'Dimmer': 60, 'Color': '996245', 'HSBColor': '21,55,60', 'Channel': [60, 38, 27]}
+The light is bright
+

The same function can be used with multiple triggers.

If the function to process an ADC input should be triggered both by the tele/SENSOR message and the result of a Status 10 command:

tasmota.add_rule("ANALOG#A1", rule_adc_1)
+tasmota.add_rule("StatusSNS#ANALOG#A1", rule_adc_1)
+

Or if the same function is used to process similar triggers:

import string
+
+def rule_adc(value, trigger)
+  var i=string.find(trigger,"#A")
+  var tr=string.split(trigger,i+2)
+  var adc=number(tr[1])
+  print("value of adc",adc," is ",value)
+end
+
+tasmota.add_rule("ANALOG#A1",rule_adc)
+tasmota.add_rule("ANALOG#A2",rule_adc)
+

Another way to address the same using anonymous functions created dynamically

def rule_adc(adc, value)
+  print("value of adc",adc," is ",value)
+end
+tasmota.add_rule("ANALOG#A1", def (value) rule_adc(1,value) end )
+tasmota.add_rule("ANALOG#A2", def (value) rule_adc(2,value) end )
+

Multiple triggers AND logic~

It is possible to combine multiple triggers in a AND logic as an array:

tasmota.add_rule(["ANALOG#A1>300","ANALOG#A1<500"], def (value) rule_adc_in_range(1,value) end )
+
would trigger if 300 < ANALOG#A1 < 500

Triggers can be of different types too:

tasmota.add_rule(["ANALOG#A1>300","BME280#Temperature>28.0"], def (value) rule_adc_and_temp(1,value) end )
+
would trigger for simultaneous ANALOG#A1>300 AND BME280#Temperature>28.0

Teleperiod rules~

Teleperiod rules are supported with a different syntax from Tasmota rules. Instead of using Tele- prefix, you must use Tele#. For example Tele#ANALOG#Temperature1 instead of Tele-ANALOG#Temperature1

Rules operators~

Operator Function
String Operators
= equal to (used for string comparison)
!== not equal to (used for string comparison)
$< string starts with
$> string ends with
$\| string contains
$! string is not equal to
$^ string do not contains
Numerical Operators
== equal to (used for numerical comparison)
> greater than
< lesser than
!= number not equal to
>= greater than or equal to
<= lesser than or equal to
\| Modulo division to this number is 0 (remainder=0)

Timers~

Berry code, when it is running, blocks the rest of Tasmota. This means that you should not block for too long, or you may encounter problems. As a rule of thumb, try to never block more than 50ms. If you need to wait longer before the next action, use timers. As you will see, timers are very easy to create thanks to Berry's functional nature.

All times are in milliseconds. You can know the current running time in milliseconds since the last boot:

> tasmota.millis()
+9977038
+

Sending a timer is as easy as tasmota.set_timer(<delay in ms>,<function>)

> def t() print("Booh!") end
+
+> tasmota.set_timer(5000, t)
+[5 seconds later]
+Booh!
+

A word on functions and closure~

Berry is a functional language, and includes the very powerful concept of a closure. In a nutshell, it means that when you create a function, it can capture the values of variables when the function was created. This roughly means that it does what intuitively you would expect it to do.

When using Rules or Timers, you always pass Berry functions.

cron recurrent calls~

You can choose to run some function/closure at regular intervals specified as cron type format. Crontab Guru is an easy way to create and test your cron format.

> def f() print("Hi") end
+> tasmota.add_cron("*/15 * * * * *", f, "every_15_s")
+Hi
+Hi      # added every 15 seconds
+> tasmota.remove_cron("every_15_s")     # cron stops
+

Like timers, you need to create a closure if you want to register a method of an instance. Example:

class A
+    var name
+    def init(name)
+        self.name = name
+    end
+    def p()
+        print("Hi,", self.name)
+    end
+end
+
> bob = A("bob")
+> bob.p()
+Hi, bob
+> tasmota.add_cron("*/15 * * * * *", /-> bob.p(), "hi_bob")
+Hi, bob
+Hi, bob
+Hi, bob
+> tasmota.remove_cron("hi_bob")     # cron stops
+

You can get the timestamp for the next event by using tasmota.next_cron(id) which returns an epoch in seconds.

Loading Filesystem~

You can upload Berry code in the filesystem using the Consoles - Manage File system menu and load them at runtime. Make careful to use *.be extension for those files.

To load a Berry file, use the load(filename) function where filename is the name of the file with .be or .bec extension; if the file has no extension '.be' is automatically appended.

You don't need to prefix with /. A leading / will be added automatically if it is not present.

When loading a Berry script, the compiled bytecode is automatically saved to the filesystem, with the extension .bec (this is similar to Python's .py/.pyc mechanism). The save(filename,closure) function is used internally to save the bytecode.

If a precompiled bytecode (extension .bec) is present of more recent than the Berry source file, the bytecode is directly loaded which is faster than compiling code. You can eventually remove the *.be file and keep only *.bec file (even with load("file.be").

Creating a Tasmota Driver~

You can easily create a complete Tasmota driver with Berry.

A Driver responds to messages from Tasmota. For each message type, the method with the same name is called. Actually you can register any class as a driver, it does not need to inherit from Driver; the call mechanism is based on names of methods that must match the name of the event to be called.

Driver methods are called with the following parameters: f(cmd, idx, payload, raw). cmd is a string, idx an integer, payload a Berry object representation of the JSON in payload (if any) or nil, raw is a string. These parameters are meaningful to a small subset of events:

  • every_second(): called every second
  • every_50ms(): called every 50ms (i.e. 20 times per second)
  • every_100ms(): called every 100ms (i.e. 10 times per second)
  • every_200ms(): called every 200ms (i.e. 5 times per second)
  • every_250ms(): called every 250ms (i.e. 4 times per second)
  • web_sensor(): display sensor information on the Web UI
  • json_append(): display sensor information in JSON format for TelePeriod reporting
  • web_add_button(): (deprecated) synonym of web_add_console_button()
  • web_add_main_button(), web_add_management_button(), web_add_console_button(), web_add_config_button(): add a button to Tasmotas Web UI on a specific page
  • web_add_handler(): called when Tasmota web server started, and the right time to call webserver.on() to add handlers
  • button_pressed(): called when a button is pressed
  • save_before_restart(): called just before a restart
  • mqtt_data(topic, idx, data, databytes): called for MQTT payloads matching mqtt.subscribe. idx is zero, and data is normally unparsed JSON.
  • set_power_handler(cmd, idx): called whenever a Power command is made. idx contains the index of the relay or light. cmd can be ignored.
  • display(): called by display driver with the following subtypes: init_driver, model, dim, power.

Then register the driver with tasmota.add_driver(<driver>).

There are basically two ways to respond to an event:

Example

Define a class and implement methods with the same name as the events you want to respond to.

class MyDriver
+  def every_second()
+    # do something
+  end
+end
+
+d1 = MyDriver()
+
+tasmota.add_driver(d1)
+

Fast Loop~

Beyond the events above, a specific mechanism is available for near-real-time events or fast loops (200 times per second, or 5ms).

Special attention is made so that there is no or very little impact on performance. Until a first callback is registered, performance is not impacted and Berry is not called. This protects any current use from any performance impact.

Once a callback is registered, it is called separately from Berry drivers to ensure minimal overhead.

tasmota.add_fast_loop(cl:function) -> nil registers a callback to be called in fast loop mode.

The callback is called without any parameter and does not need to return anything. The callback is called at each iteration of Tasmota event loop. The frequency is set to 200Hz or 5ms.

Note: since v13.1.0.2, the frequency of fast_loop does not depend anymore on the value of the Sleep <x> command.

tasmota.remove_fast_loop(cl:function) -> nil removes a previously registered function or closure. You need to pass the exact same closure reference.

Warning, if you need to register a method from an instance, you need a closure:

class my_driver
+  def every_100ms()
+    # called every 100ms via normal way
+  end
+
+  def fast_loop()
+    # called at each iteration, and needs to be registered separately and explicitly
+  end
+
+  def init()
+    # register fast_loop method
+    tasmota.add_fast_loop(/-> self.fast_loop())
+    # variant:
+    # tasmota.add_fast_loop(def () self.fast_loop() end)
+  end
+end
+
+tasmota.add_driver(my_driver())                     # register driver
+tasmota.add_fast_loop(/-> my_driver.fast_loop())    # register a closure to capture the instance of the class as well as the method
+

Tasmota Only Extensions~

log(msg:string [, level:int = 3]) -> string~

Logs a message to the Tasmota console. Optional second argument is log_level (0..4), default is 2 LOG_LEVEL_INFO.

Example

> log("A")
+A
+

load(filename:string) -> bool~

Loads a Berry script from the filesystem, and returns true if loaded successfully, false if file not found, or raises an exception in runtime. Filename does not need to start with /, but needs to end with .be (Berry source code) or .bec (precompiled bytecode).

When loading a source file, the precompiled bytecode is saved to filesystem using the .bec extension.

save(filename:string, f:closure) -> nil~

Internally used function to save bytecode. It's a wrapper to the Berry's internal API be_savecode(). There is no check made on the filename.

There is generally no need to use this function, it is used internally by load().

tasmota object~

A root level object called tasmota is created and contains numerous functions to interact with Tasmota.

Tasmota Function Parameters and details
tasmota.get_free_heap () -> int
Returns the number of free bytes on the Tasmota heap.
tasmota.publish (topic:string, payload:string[, retain:bool, start:int, len:int]) -> nil
Deprecated see mqtt.publish
tasmota.publish_result (payload:string, subtopic:string) -> nil
Publishes a JSON result and triggers any associated rule. payload is expected to be a JSON string, and subtopic the subtopic used to publish the payload.
tasmota.publish_rule (payload:string) -> handled:bool
sends a JSON stringified message to the rule engine, without actually publishing a message to MQTT. Returns true if the message was handled by a rule.
tasmota.cmd (command:string [, mute:bool]) -> map
Sends any command to Tasmota, like it was type in the console. It returns the result of the command if any, as a map parsed from the command output JSON. Takes an optional mute attribute. If mute is true, logging (serial, web, mqtt) is reduced to level 1 (only severe errors) to avoid polluting the logs.
tasmota.memory () -> map
Returns memory stats similar to the Information page.
Example: {'iram_free': 41, 'frag': 51, 'program_free': 1856, 'flash': 4096, 'heap_free': 226, 'program': 1679}
or when PSRAM {'psram_free': 3703, 'flash': 16384, 'program_free': 3008, 'program': 1854, 'psram': 4086, 'frag': 27, 'heap_free': 150}
tasmota.add_rule (trigger:string, f:function [, id:any]) ->nil
(triggers:list_of_string, f:function [, id:any]) ->nil
Adds a rule to the rule engine. See above for rule triggers.
Optional id allows to remove selectively rules with tasmota.remove_rule().
tasmota.remove_rule (trigger:string [, id:any]) ->nil
(triggers:list_of_string [, id:any]) ->nil
Removes a rule from the rule engine. Silently ignores the trigger(s) if no rule matches. Optional id to remove selectively some rules.
tasmota.add_driver (instance) ->nil
Registers an instance as a driver
tasmota.remove_driver (instance) ->nil
Removes a driver
tasmota.gc () -> int
Triggers garbage collection of Berry objects and returns the bytes currently allocated. This is for debug only and shouldn't be normally used. gc is otherwise automatically triggered when necessary.
tasmota.urlfetch (url:string [, filename:string]) -> bytes:int
Download a url (http or https) and store the content in the filesystem
filename is optional, needed if you want to change the name of the file from the url suffix. Returns the number of bytes downloaded or -1 if failed.

Functions used to retrieve Tasmota configuration~

Tasmota Function Parameters and details
tasmota.get_option (index:int) -> int
Returns the value of SetOption <index>
tasmota.wire_scan (addr:int [, index:int]) -> wire instance or nil
Scan both I2C buses for a device of address addr, optionally taking into account disabled devices via I2CDevice. Returns a wire object corresponding to the bus where the device is, or nil if device is not connected or disabled.
tasmota.i2c_enabled (index:int) -> bool
Returns true if the I2C module is enabled, see I2C page.
tasmota.arch () -> string
Returns the name of the architecture. Currently can be esp32, esp32s2, esp32s3, esp32c3
tasmota.read_sensors ([show_sensor:bool]) -> string
Returns the value of sensors as a JSON string similar to the teleperiod. The response is a string, not a JSON object. The reason is that some sensors might produce invalid JSON. It's your code's responsibility to try parsing as JSON.
An optional boolean parameter (false by default) can be set to trigger a display of the new values (i.e. sends a FUNC_SHOW_SENSOR` event to drivers).
tasmota.wifi () -> map
Retrieves Wi-Fi connection info or empty map.
Example: {'mac': 'aa:bb:cc:22:11:03', 'quality': 100, 'rssi': -47, 'ip': '192.168.1.102'}
tasmota.eth () -> map
Retrieves Ethernet connection info or empty map.
Example: {'mac': 'aa:bb:cc:22:11:00', 'ip': '192.168.1.101'}

Functions for time, timers or cron~

Tasmota Function Parameters and details
tasmota.millis ([delay:int]) -> int
Returns the number of milliseconds since last reboot. The optional parameter lets you specify the number of milliseconds in the future; useful for timers.
tasmota.time_reached (timer:int) -> bool
Checks whether the timer (in milliseconds) has been reached or not. Always use this function and don't do compares between millis() and timers, because of potential sign and overflow issues.
tasmota.rtc () -> map
Returns clockwall time with variants.
Example: {'local': 1619560407, 'utc': 1619556807, 'timezone': 60, 'restart': 1619556779}
tasmota.time_dump (timestamp:int) -> map
Decompose a timestamp value (in seconds) to its components
Example: tasmota.time_dump(1619560407) -> {'min': 53, 'weekday': 2, 'sec': 27, 'month': 4, 'year': 2021, 'day': 27, 'epoch': 1619560407, 'hour': 21}
tasmota.time_str (timestamp:int) -> string
Converts a timestamp value (in seconds) to an ISO 8601 string
Example: tasmota.time_str(1619560407) -> 2021-04-27T21:53:27
tasmota.set_timer (delay:int, f:function [, id:any]) -> nil
Runs the closure or function f after delay milliseconds, optional id can be used to remove the timer.
tasmota.remove_timer (id:string) -> nil
Removes the timer with the id used on tasmota.set_timer.
tasmota.strftime (format:string, timestamp:int) -> string
Converts a timestamp value (in seconds) to a string using the format conversion specifiers
Example: tasmota.strftime("%d %B %Y %H:%M:%S", 1619560407) -> 27 April 2021 21:53:27
tasmota.strptime (time:string, format:string) -> map or nil
Converts a string to a date, according to a time format following the C strptime format. Returns a map similar to tasmota.time_dump() or nil if parsing failed. An additional unparsed attribute reports the unparsed string, or empty string if everything was parsed.
Example: tasmota.strptime("2001-11-12 18:31:01", "%Y-%m-%d %H:%M:%S") -> {'unparsed': '', 'weekday': 1, 'day': 12, 'epoch': 1005589861, 'min': 31, 'year': 2001, 'month': 11, 'sec': 1, 'hour': 18}
tasmota.yield () -> nil
Calls Arduino framework yield() function to give back some time to low-level functions, like Wifi. Prevents WDT watchdog from happening.
tasmota.delay ([delay:int]) -> int
Waits and blocks execution for delay milliseconds. Should ideally never wait more than 10ms and absolute max 50ms. Otherwise use set_timer.
tasmota.add_cron (pattern:string, f:function [, id:any]) -> nil
Adds a cron-type timer, with a cron pattern and a function/closure to call. An optional id can be added to retrieve or delete the cron timer
tasmota.remove_cron (id:any) -> nil
Remove a cron timer.
tasmota.next_cron (id:any) -> int
returns the next timestamp for the cron timer. The timestamp is second epoch in local time. You can use tasmota.tasmota.time_str() to convert to a time string.

Functions to create custom Tasmota command~

Tasmota Function Parameters and details
tasmota.add_cmd (name:string, f:function) -> nil
Adds a command to Tasmota commands. Command names are case-insensitive. Command names are analyzed after native commands and after most commands, so you can't override a native command.
tasmota.resp_cmnd_str (message:string) -> nil
Sets the output for the command to message.
tasmota.resp_cmnd_done () -> nil
Sets the output for the command to "Done" (localized message).
tasmota.resp_cmnd_error () -> nil
Sets the output for the command to "Error" (localized message).
tasmota.resp_cmnd_failed () -> nil
Sets the output for the command to "Fail" (localized message).
tasmota.resp_cmnd (message:string) -> nil
Overrides the entire command response. Should be a valid JSON string.
tasmota.remove_cmd (name:string) -> nil
Remove a command to Tasmota commands. Removing an non-existing command is skipped silently.

Functions to add custom responses to JSON and Web UI to sensors~

Tasmota Function Parameters and details
tasmota.response_append (name:string) -> nil
Adds JSON fragment to the current response. Used for example for sensors to add JSON to teleperiod.
Can be called only in json_append() method of a registered driver (see cookbook). It is called at least at each teleperiod, or when reading sensor data in JSON.
tasmota.web_send (message:string) -> nil
Adds an HTML fragment to the Web output.
Can be called only in web_sensor() method of a registered driver (see cookbook). It is called at each main page refresh.
tasmota.web_send_decimal (message:string) -> nil
Adds an HTML fragment to the Web output, similar to web_send but converts decimal dot . to the locale decimal separator.
Can be called only in web_sensor() method of a registered driver (see cookbook). It is called at each main page refresh.

See examples in the Berry-Cookbook

Functions to manage Relays and Lights~

Tasmota Function Parameters and details
tasmota.get_power () -> list[bool]
Returns the state On/Off of each Relay and Light as a list of bool.
tasmota.set_power (index:int, onoff:bool) -> bool
Sets the on/off state of a Relay/Light. Returns the previous status of the Relay/Light of nil if index is invalid.
Example:
> tasmota.get_power()
[true]
tasmota.get_light deprecated use light.get
tasmota.set_light deprecated use light.set
tasmota.get_switches () -> list(bool)
Returns as many values as switches are present. true means PRESSED and false means NOT_PRESSED. (Warning: this is the opposite of the internal representation where PRESSED=0)
Note: if there are holes in the switch definition, the values will be skipped. I.e. if you define SWITCH1 and SWITCH3, the array will return the two consecutive values for switches 1/3.

Low-level access to Tasmota globals and settings.~

Use with care and only if you know what you are doing.

The construct is to use tasmota.global or tasmota.settings to read or write attributes.

You can do bad things with these features

Value Details
tasmota.global.sleep Current sleep value
tasmota.global.devices_present Number of Power channels, e.g. having virtual relays
tasmota.settings.sleep Sleep value stored in flash

mqtt module~

Use with import mqtt.

Since v11.1.0.1, there is an easier way than registering a driver, and listening to mqtt_data event. You can now just attach a function or closure to a MQTT topic, and it does the magic for you.

The function you attach to a topic pattern received only the matching MQTT messages, not all messages unlike mqtt_data() would.

The function takes the same parameters as mqtt_data(): - topic: full topic received from the broker - idx: not used - payload_s: payload as string, usually converted to JSON with import json json.load(payloas_s) - payload_b: payload as a binary payload, bytes() array - the function should return true if the event was parsed or if the event should not trigger a Tasmota command. If you return nil or nothing, it is considered as true which is the usual behavior you want (i.e. not trigger a Tasmota command from random MQTT messages).

Tasmota Function Parameters and details
mqtt.publish (topic:string, payload:string[, retain:bool, start:int, len:int]) -> nil
Equivalent of publish command, publishes a MQTT message on topic with payload. Optional retain parameter.
payload can be a string or a bytes() binary array
start and len allow to specify a sub-part of the string or bytes buffer, useful when sending only a portion of a larger buffer.
mqtt.subscribe mqtt.subscribe(topic:string [, function:closure]) -> nil
Subscribes to a topic (exact match or pattern). Contrary to Tasmota's Subscribe command, the topic is sent as-is and not appended with /#. You need to add wildcards yourself. Driver method mqtt_data is called for each matching payload.
If a function/closure is added, the function is called whenever and only if an incoming messages matches the pattern for this function. The function should return true if message was processed, false if not which will let the message flow to Tasmota eventually as a command.
mqtt.unsubscribe (topic:string) -> nil
Unubscribe to a topic (exact match).

light object~

Module light is automatically imported via a hidden import light command.

Tasmota Function Parameters and details
light.get (index:int) -> map
Get the current status if light number index (default:0).
Example:
> light.get
{'bri': 77, 'hue': 21, 'power': true, 'sat': 140, 'rgb': '4D3223', 'channels': [77, 50, 35]}
light.set (settings:map[, index:int]) -> map
Sets the current state for light index (default: 0.
Example:
> light.set({'hue':120,'bri':50,'power':true})
{'bri': 50, 'hue': 120, 'power': true, 'sat': 140, 'rgb': '173217', 'channels': [23, 50, 23]}
light.gamma10 (channel) -> int
Computes the gamma corrected value with 10 bits resolution for input and output. Note: Gamma is optimized for speed and smooth fading, and is not 100% mathematically accurate.
Input and output are in range 0..1023.
light.reverse_gamma10 (gamma) -> int
Computes the reverse gamma with 10 bits resolution for input and output.
Input and output are in range 0..1023.
light.gamma8 (channel) -> int
Computes the gamma corrected value with 8 bits resolution for input and output.
Input and output are in range 0..255.

gpio module~

This module allows to retrieve the GPIO configuration set in the templates. You need to distinguish between logical gpio (like PWM, or I2C) and physical gpio which represent the GPIO number of the physical pin. gpio.pin() transforms a logical GPIO to a physical GPIO, or -1 if the logical GPIO is not set.

Currently there is limited support for GPIO: you can only read/write in digital mode and set the GPIO mode.

Tasmota Function Parameters and details
gpio.pin_used (gpio [,index]) -> bool
returns if a specific GPIO is used. index allows to iterate through GPIOs. Example: gpio.pin_used(gpio.REL1) to check Relay1, or gpio.pin_used(gpio.REL1,1) to check Relay2 (index is zero-based)
gpio.pin (gpio [,index]) -> int
returns the physical GPIO number assigned to the Tasmota GPIO, or -1 if the GPIO is not assigned
gpio.digital_write (phy_gpio, val) -> nil needs the physical GPIO number
sets the GPIO to LOW/HIGH. val can be 0, 1, gpio.LOW or gpio.HIGH. Example: gpio.digital_write(gpio.pin(gpio.REL1), gpio.HIGH) sets Relay1 to High.
gpio.digital_read (phy_gpio) -> int needs the physical GPIO number
reads the value of a GPIO. Returns 0 or 1.
gpio.pin_mode (phy_gpio, mode) -> nil needs the physical GPIO number
Changes the GPIO mode. It should be called very cautiously. Normally Tasmota handles automatically GPIO modes.
mode can have the following values: gpio.INPUT, gpio.OUTPUT, gpio.PULLUP, gpio.INPUT_PULLUP, gpio.PULLDOWN, gpio.OPEN_DRAIN, gpio.OUTPUT_OPEN_DRAIN, gpio.DAC
gpio.dac_voltage (phy_gpio:int, voltage_mv:int) -> int
Sets the DAC voltage in mV. The resolution is 8 bits over a range of 0..3.3V, i.e. an increment of ~13mV, this function returns the actual voltage output rounded to the closest value. See below for constraints of DAC GPIOs.
gpio.set_pwm (phy_gpio:int, duty:int [, phase:int]) -> nil
Sets the value of a PWM output
phy_gpio: physical GPIO number
duty: analog value for the pwm, range is 0..1023 unless you change the PWM range
phase: (opt) set the starting point in time for this pulse from start of cycle. Range is 0..1023 unless you change PWM range. This allows to dephase pulses, for example for H-bridge.
Low-level this is a low-level function that bypasses all the Tasmota logic around PWM. Use with caution as a PWM command might overwrite your settings.

Any internal error or using unsupported GPIO yields a Berry exception.

Possible values for Tasmota GPIOs:

gpio.NONE, gpio.KEY1, gpio.KEY1_NP, gpio.KEY1_INV, gpio.KEY1_INV_NP, gpio.SWT1, gpio.SWT1_NP, gpio.REL1, gpio.REL1_INV, gpio.LED1, gpio.LED1_INV, gpio.CNTR1, gpio.CNTR1_NP, gpio.PWM1, gpio.PWM1_INV, gpio.BUZZER, gpio.BUZZER_INV, gpio.LEDLNK, gpio.LEDLNK_INV, gpio.I2C_SCL, gpio.I2C_SDA, gpio.SPI_MISO, gpio.SPI_MOSI, gpio.SPI_CLK, gpio.SPI_CS, gpio.SPI_DC, gpio.SSPI_MISO, gpio.SSPI_MOSI, gpio.SSPI_SCLK, gpio.SSPI_CS, gpio.SSPI_DC, gpio.BACKLIGHT, gpio.OLED_RESET, gpio.IRSEND, gpio.IRRECV, gpio.RFSEND, gpio.RFRECV, gpio.DHT11, gpio.DHT22, gpio.SI7021, gpio.DHT11_OUT, gpio.DSB, gpio.DSB_OUT, gpio.WS2812, gpio.MHZ_TXD, gpio.MHZ_RXD, gpio.PZEM0XX_TX, gpio.PZEM004_RX, gpio.PZEM016_RX, gpio.PZEM017_RX, gpio.SAIR_TX, gpio.SAIR_RX, gpio.PMS5003_TX, gpio.PMS5003_RX, gpio.SDS0X1_TX, gpio.SDS0X1_RX, gpio.SBR_TX, gpio.SBR_RX, gpio.SR04_TRIG, gpio.SR04_ECHO, gpio.SDM120_TX, gpio.SDM120_RX, gpio.SDM630_TX, gpio.SDM630_RX, gpio.TM1638CLK, gpio.TM1638DIO, gpio.TM1638STB, gpio.MP3_DFR562, gpio.HX711_SCK, gpio.HX711_DAT, gpio.TX2X_TXD_BLACK, gpio.TUYA_TX, gpio.TUYA_RX, gpio.MGC3130_XFER, gpio.MGC3130_RESET, gpio.RF_SENSOR, gpio.AZ_TXD, gpio.AZ_RXD, gpio.MAX31855CS, gpio.MAX31855CLK, gpio.MAX31855DO, gpio.NRG_SEL, gpio.NRG_SEL_INV, gpio.NRG_CF1, gpio.HLW_CF, gpio.HJL_CF, gpio.MCP39F5_TX, gpio.MCP39F5_RX, gpio.MCP39F5_RST, gpio.PN532_TXD, gpio.PN532_RXD, gpio.SM16716_CLK, gpio.SM16716_DAT, gpio.SM16716_SEL, gpio.DI, gpio.DCKI, gpio.CSE7766_TX, gpio.CSE7766_RX, gpio.ARIRFRCV, gpio.ARIRFSEL, gpio.TXD, gpio.RXD, gpio.ROT1A, gpio.ROT1B, gpio.ADC_JOY, gpio.SSPI_MAX31865_CS1, gpio.HRE_CLOCK, gpio.HRE_DATA, gpio.ADE7953_IRQ, gpio.SOLAXX1_TX, gpio.SOLAXX1_RX, gpio.ZIGBEE_TX, gpio.ZIGBEE_RX, gpio.RDM6300_RX, gpio.IBEACON_TX, gpio.IBEACON_RX, gpio.A4988_DIR, gpio.A4988_STP, gpio.A4988_ENA, gpio.A4988_MS1, gpio.OUTPUT_HI, gpio.OUTPUT_LO, gpio.DDS2382_TX, gpio.DDS2382_RX, gpio.DDSU666_TX, gpio.DDSU666_RX, gpio.SM2135_CLK, gpio.SM2135_DAT, gpio.DEEPSLEEP, gpio.EXS_ENABLE, gpio.TASMOTACLIENT_TXD, gpio.TASMOTACLIENT_RXD, gpio.TASMOTACLIENT_RST, gpio.TASMOTACLIENT_RST_INV, gpio.HPMA_RX, gpio.HPMA_TX, gpio.GPS_RX, gpio.GPS_TX, gpio.HM10_RX, gpio.HM10_TX, gpio.LE01MR_RX, gpio.LE01MR_TX, gpio.CC1101_GDO0, gpio.CC1101_GDO2, gpio.HRXL_RX, gpio.ELECTRIQ_MOODL_TX, gpio.AS3935, gpio.ADC_INPUT, gpio.ADC_TEMP, gpio.ADC_LIGHT, gpio.ADC_BUTTON, gpio.ADC_BUTTON_INV, gpio.ADC_RANGE, gpio.ADC_CT_POWER, gpio.WEBCAM_PWDN, gpio.WEBCAM_RESET, gpio.WEBCAM_XCLK, gpio.WEBCAM_SIOD, gpio.WEBCAM_SIOC, gpio.WEBCAM_DATA, gpio.WEBCAM_VSYNC, gpio.WEBCAM_HREF, gpio.WEBCAM_PCLK, gpio.WEBCAM_PSCLK, gpio.WEBCAM_HSD, gpio.WEBCAM_PSRCS, gpio.BOILER_OT_RX, gpio.BOILER_OT_TX, gpio.WINDMETER_SPEED, gpio.KEY1_TC, gpio.BL0940_RX, gpio.TCP_TX, gpio.TCP_RX, gpio.ETH_PHY_POWER, gpio.ETH_PHY_MDC, gpio.ETH_PHY_MDIO, gpio.TELEINFO_RX, gpio.TELEINFO_ENABLE, gpio.LMT01, gpio.IEM3000_TX, gpio.IEM3000_RX, gpio.ZIGBEE_RST, gpio.DYP_RX, gpio.MIEL_HVAC_TX, gpio.MIEL_HVAC_RX, gpio.WE517_TX, gpio.WE517_RX, gpio.AS608_TX, gpio.AS608_RX, gpio.SHELLY_DIMMER_BOOT0, gpio.SHELLY_DIMMER_RST_INV, gpio.RC522_RST, gpio.P9813_CLK, gpio.P9813_DAT, gpio.OPTION_A, gpio.FTC532, gpio.RC522_CS, gpio.NRF24_CS, gpio.NRF24_DC, gpio.ILI9341_CS, gpio.ILI9341_DC, gpio.ILI9488_CS, gpio.EPAPER29_CS, gpio.EPAPER42_CS, gpio.SSD1351_CS, gpio.RA8876_CS, gpio.ST7789_CS, gpio.ST7789_DC, gpio.SSD1331_CS, gpio.SSD1331_DC, gpio.SDCARD_CS, gpio.ROT1A_NP, gpio.ROT1B_NP, gpio.ADC_PH, gpio.BS814_CLK, gpio.BS814_DAT, gpio.WIEGAND_D0, gpio.WIEGAND_D1, gpio.NEOPOOL_TX, gpio.NEOPOOL_RX, gpio.SDM72_TX, gpio.SDM72_RX, gpio.TM1637CLK, gpio.TM1637DIO, gpio.PROJECTOR_CTRL_TX, gpio.PROJECTOR_CTRL_RX, gpio.SSD1351_DC, gpio.XPT2046_CS, gpio.CSE7761_TX, gpio.CSE7761_RX, gpio.VL53LXX_XSHUT1, gpio.MAX7219CLK, gpio.MAX7219DIN, gpio.MAX7219CS, gpio.TFMINIPLUS_TX, gpio.TFMINIPLUS_RX, gpio.ZEROCROSS, gpio.HALLEFFECT, gpio.EPD_DATA, gpio.INPUT, gpio.KEY1_PD, gpio.KEY1_INV_PD, gpio.SWT1_PD, gpio.I2S_OUT_DATA, gpio.I2S_OUT_CLK, gpio.I2S_OUT_SLCT, gpio.I2S_IN_DATA, gpio.I2S_IN_CLK, gpio.I2S_IN_SLCT, gpio.INTERRUPT, gpio.MCP2515_CS, gpio.HRG15_TX, gpio.VINDRIKTNING_RX, gpio.BL0939_RX, gpio.BL0942_RX, gpio.HM330X_SET, gpio.HEARTBEAT, gpio.HEARTBEAT_INV, gpio.SHIFT595_SRCLK, gpio.SHIFT595_RCLK, gpio.SHIFT595_OE, gpio.SHIFT595_SER, gpio.SOLAXX1_RTS, gpio.OPTION_E, gpio.SDM230_TX, gpio.SDM230_RX, gpio.ADC_MQ, gpio.CM11_TXD, gpio.CM11_RXD, gpio.BL6523_TX, gpio.BL6523_RX, gpio.ADE7880_IRQ, gpio.RESET, gpio.MS01, gpio.SDIO_CMD, gpio.SDIO_CLK, gpio.SDIO_D0, gpio.SDIO_D1, gpio.SDIO_D2, gpio.SDIO_D3, gpio.FLOWRATEMETER_SIGNAL, gpio.SENSOR_END

An H-bridge is an electronic circuit that switches the polarity of a voltage applied to a load. These circuits are often used in robotics and other applications to allow DC motors to run forwards or backwards.

See the Berry cookbook for H-bridge control

DAC GPIOs~

DAC is limited to specific GPIOs:

  • ESP32: only GPIO 25-26
  • ESP32-S2: only GPIO 17-18
  • ESP32-C3: not supported

Example

> gpio.pin_mode(25, gpio.DAC)   # sets GPIO25 to a DAC pin
+> gpio.dac_voltage(25, 1250)    # set voltage to 1250mV
+1255
+
The function returns the closest voltage found. In this case it's 1255 for setting to 1250.

I2S~

DAC can also be used via Esp8266Audio through the ESP32 I2S -> DAC bridge.

Example
class MP3_Player : Driver
+  var audio_output, audio_mp3
+  def init()
+    self.audio_output = AudioOutputI2S(
+      gpio.pin(gpio.I2S_OUT_CLK),
+      gpio.pin(gpio.I2S_OUT_SLCT),
+      gpio.pin(gpio.I2S_OUT_DATA),
+      0,    #- I2S port -#
+      64)    #- number of DMA buffers of 64 bytes each, this is the value required since we update every 50ms -#
+    self.audio_mp3 = AudioGeneratorMP3()
+  end
+
+  def play(mp3_fname)
+    if self.audio_mp3.isrunning()
+      self.audio_mp3.stop()
+    end
+    var audio_file = AudioFileSourceFS(mp3_fname)
+    self.audio_mp3.begin(audio_file, self.audio_output)
+    self.audio_mp3.loop()    #- start playing now -#
+  end
+
+  def every_50ms()
+    if self.audio_mp3.isrunning()
+      self.audio_mp3.loop()
+    end
+  end
+end
+
+mp3_player = MP3_Player()
+tasmota.add_driver(mp3_player)
+
+mp3_player.play("/pno-cs.mp3")
+

energy module~

The energy module provides ways to read current energy counters and values (if you're creating your own automation) or updating the energy counters (if you're writing a driver).

It relies on a new Berry feature that provides a direct mapping between the internal C structure called struct Energy and the energy module in Berry.

For example, if you want to read or update an energy value:

> energy.active_power
+0
+> energy.active_power = 460
+> energy.active_power
+460
+
+# internally it updates the C value `Energy.active_power[0]` (float)
+

You don't need to do import energy since Tasmota does it for you at boot.

The special energy.read() function dumps all current values to a single map. Be aware that the object is very long. Prefer accessing individual attributes instead.

Tasmota Function Parameters and details
energy.read() () -> map
Returns all current values for the energy module. Some values may be unused by the current driver.

List of energy attributes that you can read or write:

Attribute Type Description
voltage
voltage_2
voltage_3
float Voltage (V) for main phase or 3 phases
current
current_2
current_3
float Current (A) for main phase or 3 phases
active_power
active_power_2
active_power_3
float Active Power (W) for main phase or 3 phases
reactive_power
reactive_power_2
reactive_power_3
float Reactive Power (W) for main phase or 3 phases
power_factor
power_factor_2
power_factor_3
float Power Factor (no unit) for main phase or 3 phases
frequency
frequency_2
frequency_3
float Frequency (Hz) for main phase or 3 phases
export_active
export_active_2
export_active_3
float (kWh)
start_energy float Total previous energy (kWh)
daily float Daily energy (kWh)
total float Total energy (kWh)
today_delta_kwh uint32 (deca milli Watt hours)
5764 = 0.05764 kWh = 0.058 kWh
today_offset_kwh uint32 (deca milli Watt hours)
today_kwh uint32 (deca milli Watt hours)
period uint32 (deca milli Watt hours)
fifth_second uint8
command_code uint8
data_valid
data_valid_2
data_valid_3
uint8
phase_count uint8 Number of phases (1,2 or 3)
voltage_common bool Use single voltage
frequency_common bool Use single frequency
use_overtemp bool Use global temperature as overtemp trigger on internal energy monitor hardware
today_offset_init_kwh bool
voltage_available bool Enable if voltage is measured
current_available bool Enable if current is measured
type_dc bool
power_on bool
power_history_0
power_history_0_2
power_history_0_3
power_history_1
power_history_1_2
power_history_1_3
power_history_2
power_history_2_2
power_history_2_3
uint16
power_steady_counter uint8 Allow for power on stabilization
min_power_flag bool
max_power_flag bool
min_voltage_flag bool
max_voltage_flag bool
min_current_flag bool
max_current_flag bool
mplh_counter uint16
mplw_counter uint16
mplr_counter uint8
max_energy_state uint8

wire object for I2C~

Berry Scripting provides 2 objects: wire1 and wire2 to communicate with both I2C buses.

Use wire1.scan() and wire2.scan() to scan both buses:

> wire1.scan()
+[]
+
+> wire2.scan()
+[140]
+

You generally use tasmota.wire_scan() to find a device and the corresponding I2C bus.

MPU6886 on bus 2

> mpuwire = tasmota.wire_scan(0x68, 58)
+> mpuwire
+<instance: Wire()>
+
Wire Function Parameters and details
bus read only attribute, 1 or 2
Bus number for this wire instance.
enabled () -> bool
Returns true is the I2C bus is initialized (i.e. GPIOs are defined)
scan () -> array of int
Scan the bus and return all responding addresses. Note: addresses are displayed as decimal ints, not hex.
scan () -> array of int
Scan the bus and return all responding addresses. Note: addresses are displayed as decimal ints, not hex.
detect (addr:int) -> bool
Returns true if the device of address addr is connected to this bus.
read (addr:int, reg:int, size:int) -> int or nil
Read a value of 1..4 bytes from address addr and register reg. Returns nil if no response.
write (addr:int, reg:int, val:int, size:int) -> bool
Writes a value of 1..4 bytes to address addr, register reg with value val. Returns true if successful, false if not.
read_bytes (addr:int, reg:int ,size:int) -> instance of bytes()
Reads a sequence of size bytes from address addr register reg. Result is a bytes() instance or bytes() if not successful.`
write_bytes (addr:int, reg:int, val:bytes) -> nil
Writes the val bytes sequence as bytes() to address addr register reg.

Low-level commands if you need finer control:

Wire Function Parameters and details
_begin_transmission (address:int) -> nil
_end_transmission ([stop:bool]) -> nil
Send stop if stop is true.
_request_from (addr:int, size:int [stop:bool = true]) -> nil
_available () -> bool
_read read() -> int
Reads a single byte.
_write (value:int or s:string) -> nil
Sends either single byte or an arbitrary string.

path module~

A simplified version of os.path module of standard Berry which is disabled in Tasmota because we don't have a full OS.

The default file-system is the ESP32 internal flash. If you have a SD card mounted, it is mapped to the /sd/ subdirectory.

Example:

import path
+print(path.listdir("/sd/"))
+# outputs a list of filenames at the root dir of the SD card
+
Tasmota Function Parameters and details
path.exists (file_name:string) -> bool
Returns true if the file exists. You don't need to prefix with /, as it will automatically be added if the file does not start with /
path.last_modified (file_name:string) -> int
Returns the timestamp when the file was last modified, or nil if the file does not exist. You don't need to prefix with /, as it will automatically be added if the file does not start with /
path.listdir (dir_name:string) -> list(string)
List a directory, typically root dir "/" and returns a list of filenames in the directory. Returns an empty list if the directory is invalid
path.remove (file_name:string) -> bool
Deletes a file by name, return true if successful
path.format (true:bool) -> bool
Re-formats the LittleFS file system (internal ESP32 flash) and erases all content. The parameter needs to be true as to avoid unwanted calls. Returns true if reformatting was successful.
This is sometimes useful when the file-system becomes unstable or corrupt after multiple re-partitionings.

persist module~

Easy way to persist simple values in Berry and read/write any attribute. Values are written in JSON format in _persist.json file.

Example

import persist
persist.a = 1
persist.b = "foobar"
print(persist)

persist.save() # save to _persist.json

Tasmota Function Parameters and details
persist.save ()
triggers saving to file system. It is called automatically before a restart but you might want to call it yourself to prevent losing data in case of power loss or crash. persist.save() writes to flash, so be careful of not calling it too often, or it will cause wearing of flash and reduce its lifetime.
persist.has (param:string) -> bool
returns true or false if the key exists
persist.remove (param:string) -> bool
removes a key or ignores if key doesn't exist
persist.find my_param:string [, "default value"] -> string | bool
returns the value for a key, or nil or the default value. Similar to map.find

introspect module~

Allows to do introspection on instances and modules, to programmatically list attributes, set and get them.

> class A var a,b def f() return 1 end end
+> ins=A()
+> ins.a = "foo"
+> import introspect
+
+> introspect.members(ins)
+['b', 'a', 'f']
+
+> introspect.get(ins, "a")
+foo
+
+> introspect.set(ins, "a", "bar")
+bar
+
+> ins.a
+bar
+
Tasmota Function Parameters and details
introspect.members (nil | instance | module | class) -> list
Returns the list of members of the object. If nil is passed, it returns the list of globals (similar to global module). Note: virtual dynamic members are not listed.
introspect.get (instance | module, name:string) -> any
Returns the member of name name or nil if it does not exist. Note: virtual dynamic members are not yet supported. Note2: classes are not yet supported.
introspect.set (instance | module, name:string, value:any) -> any
Sets the member of name name to value or ignores the call if the member does not exist. Note: virtual dynamic members are not yet supported.
introspect.module (name:string) -> module or nil
Loads a module by name or return nil if not found. The import command works only for static predefined names, this addition makes it dynamic. Contrary to import command, this function does not create an entry in the current scope (i.e. does not either create a global variable with the module's name).
introspect.toptr (any) -> comptr
Converts an int to a comptr pointer. This is sage in Tasmota since pointers and ints are both 32 bits in size.
If argument is a general object, this returns a pointer to the object, and can be converted back to the original object with introspect.fromptr.
introspect.fromptr (comptr) -> any
Converts a comptr pointer to its original object.
Warning: this operation is considered dagerous and should be used with extreme care. If the pointer is invalid or the object was garbage collected, Tasmota will crash.
introspect.ismethod (function or closure) -> bool
Returns true if the function passed as argument is a method of a class, or false if the argument is a simple function or a static method.
This is typically used to check callbacks and make sure that you don't pass a method as argument; methods typically need to be wrapped in a closure to capture the target object.

webclient class~

Class webclient provides an implementation of an HTTP/HTTPS web client and make requests on the LAN or over the Internet.

Features:

  • Support HTTP and HTTPS requests to IPv4 addresses and domain names, to arbitrary ports, via a full URL.
  • Support for HTTPS and TLS via BearSSL (which is much lighter than default mbedTLS)
  • HTTPS (TLS) only supports cipher ECDHE_RSA_WITH_AES_128_GCM_SHA256 which is both secure and widely supported
  • Support for URL redirections
  • Ability to set custom User-Agent
  • Ability to set custom headers
  • Ability to set Authentication header
  • Support for Chunked encoding response (so works well with Tasmota devices)
  • Support for GET, POST, PUT, PATCH, DELETE methods

The current implementation is based on a fork of Arduino's HttpClient customized to use BearSSL

Current limitations (if you need extra features please open a feature request on GitHub):

  • Payload sent to server (POST) can include either text or binary
  • Only supports text responses (html, json...) but not binary content yet (no NULL char allowed). However you can download binary content to the file-system with write_file
  • Maximum response size is 32KB, requests are dropped if larger
  • HTTPS (TLS) is in 'insecure' mode and does not check the server's certificate; it is subject to Man-in-the-Middle attack
  • No support for compressed response - this should not be a problem since the client does not advertize support for compressed responses

Example

> cl = webclient()
+> cl.begin("http://ota.tasmota.com/tasmota32/release/")
+<instance: webclient()>
+
+> r = cl.GET()
+> print(r)
+200
+
+> s = cl.get_string()
+> print(s)
+<pre>
+<b></b>Alternative firmware for ESP32 based devices with web UI,
+[.../...]
+

Example

> cl = webclient()
+> cl.begin("https://raw.githubusercontent.com/tasmota/autoconf/main/esp32/M5Stack_Fire_autoconf.zip")
+<instance: webclient()>
+
+> r = cl.GET()
+> print(r)
+200
+
+> cl.write_file("M5Stack_Fire_autoconf.zip")
+950
+

Managing redirects~

HTTP redirects (301/302) are not followed by default. You can use wc.set_follow_redirects(true) to have redirects automatically followed for HEAD and GET. There is a default limit of 10 successive redirects, this prevents from infinite loops.

For the examples, we use http://ota.tasmota.com/tasmota32 which is redirected to http://ota.tasmota.com/tasmota32/

Example

cl = webclient()
+cl.set_follow_redirects(true)
+cl.begin("http://ota.tasmota.com/tasmota32")
+r = cl.GET()
+print(r)
+s = cl.get_string()
+print(s)
+

Alternatively, you can manage yourself redirects and retrieve the Location header

Example

cl = webclient()
+cl.set_follow_redirects(false)
+cl.collect_headers("Location")
+cl.begin("http://ota.tasmota.com/tasmota32")
+r = cl.GET()
+print(r)
+if r == 301 || r == 302
+  print("Location:", cl.get_header("Location"))
+elif r == 200
+  s = cl.get_string()
+  print(s)
+end
+cl.close()
+

Main functions:

webclient method Parameters and details
begin (url:string) -> self
Set the complete URL, including protocol (http or https), IPv4 or domain name, port... This should be the first call. The connection is not established at this point.
Use url_encode() prior to sending a URL if it requires URL encoding.
GET () -> result_code:int
Establish a connection to server, send GET request and wait for response header.
Returns the HTTP result code or an error code if negative, 200 means OK.
POST (payload:string or bytes) -> result_code:int
Establish a connection to server, send POST request with payload and wait for response header.
Returns the HTTP result code or an error code if negative, 200 means OK.
PUT (payload:string or bytes) -> result_code:int
Establish a connection to server, send PUT request with payload and wait for response header.
Returns the HTTP result code or an error code if negative, 200 means OK.
PATCH (payload:string or bytes) -> result_code:int
Establish a connection to server, send PATCH request with payload and wait for response header.
Returns the HTTP result code or an error code if negative, 200 means OK.
DELETE (payload:string or bytes) -> result_code:int
Establish a connection to server, send DELETE request with payload and wait for response header.
Returns the HTTP result code or an error code if negative, 200 means OK.
get_size () -> int
Once a connection succeeded (GET or POST), reads the size of the response as returned by the server in headers (before actually reading the content). A value -1 means that the response size is unknown until you read it.
get_string () -> string
Once a connection succeeded (GET or POST), reads the content of the response in a string. The response max size is 32KB, any response larger is dropped. Connection is closed and resources are freed after this call completes.
close () -> nil
Closes the connection and frees buffers. close can be called after GET or POST and is implicitly called by get_string. You don't usually need to use close unless you are only retrieving the result_code for a request and not interested in the content.
write_file (file_name:string) -> int
Downloads the binary content of the resource and stores it on the file system. Returns the number of bytes downloaded or -1 if an error occurred

Request customization:

webclient method Parameters and details
add_header (name:string, value:string [, first:bool=false [, replace:bool=true]]) -> nil
Sets an arbitrary header for name:value.
first moves the header in the first place, replace replaces a header with the same name or adds one line if false.
set_timeouts (req_timeout:int [, tcp_timeout:int]) -> self
Sets the request timeout in ms and optionally the TCP connection timeout in ms.
set_useragent (useragent:string) -> self
Sets the User-Agent header used in request.
set_auth (auth:string) or (user:string, password:string) -> self
Sets the authentication header, either using pre-encoded string, or standard user/password encoding.
set_follow_redirects (bool) -> self
Enables or disables redirects following.
If false: (HTTPC_DISABLE_FOLLOW_REDIRECTS) no redirection will be followed.
If true: (HTTPC_STRICT_FOLLOW_REDIRECTS) strict RFC2616, only requests using GET or HEAD methods will be redirected (using the same method), since the RFC requires end-user confirmation in other cases.
There is a default limit of 10 successive redirects, this prevents from infinite loops.
collect_headers ( [header_name:string]* ) -> self
Registers a list of header names that needs to be collected from the response. Pass multiple strings as separate arguments (not as a list).
get_header (header_name:string) -> string
Returns the header value for a header name (case sensitive). Returns "" (empty string) if no header.

Static utility methods:

webclient static method Parameters and details
url_encode (url:string) -> string
Encodes a string according to URL escape rules. Use before you use begin()

webserver module~

Module webserver provides functions to enrich Tasmota's Web UI. It is tightly linked to Tasmota page layout.

Functions used to add UI elements like buttons to Tasmota pages, and analyze the current request. See above Driver to add buttons to Tasmota UI.

General Function Parameters and details
arg_size () -> int
Returns the number of arguments in the request
arg (arg_name:string or arg_index:int): -> string
Returns the value of the argument either by name or by position number [0..arg_size()-1]. If an argument has multiple values, you need to iterate using ints to get all values
arg_name (arg_index:int) -> string
Returns the name of argument by index [0..arg_size()-1]
has_arg (arg_name:string): -> bool
Checks if an argument with this name exists
check_privileged_access () -> bool
Returns true if the page needs privileged access
content_send (string) -> nil
Sends the HTML content to the client. Tasmota uses Chunked encoding, which means than the content is regularly sent to the client and not buffered in Tasmota's memory
content_button ([button:int]) -> nil
Displays a standard button by code, using Tasmota localization. Possible values are webserver.BUTTON_CONFIGURATION, webserver.BUTTON_INFORMATION, webserver.BUTTON_MAIN, webserver.BUTTON_MANAGEMENT, webserver.BUTTON_MODULE. Default is webserver.BUTTON_MAIN.
html_escape (string) -> string
Escapes characters to safe HTML.

Low-level functions if you want to display custom pages and content:

General Function Parameters and details
on (prefix:string, callback:closure [, method:int]) -> nil
Attaches a handler (any closure or function) to a prefix. An optional method argument (defaults to webserver.HTTP_ANY specifies the HTTP methods to be received (ANY, GET, POST, OPTIONS, POST)
WARNING - this should be called only when receiving web_add_handler event. If called before the WebServer is set up and Wi-Fi on, it will crash. For debug purpose, it can be called later when you are sure that Wi-Fi or Ethernet is up.
state () -> int
Returns the internal state of Tasmota web server. Possible values are webserver.HTTP_OFF, webserver.HTTP_USER, webserver.HTTP_ADMIN, webserver.HTTP_MANAGER, webserver.HTTP_MANAGER_RESET_ONLY.
content_start () -> nil
Start response page
content_response (string) -> nil
Sends a response to a XMLHttpRequest
content_send_style () -> nil
Sends the standard Tasmota style
content_flush () -> nil
Flush the buffer and send any buffered content to the client
content_stop () -> nil
End of the response, closes the connection

Module webserver also defines the following constants:

  • Tasmota's web server states: webserver.HTTP_OFF, webserver.HTTP_USER, webserver.HTTP_ADMIN, webserver.HTTP_MANAGER, webserver.HTTP_MANAGER_RESET_ONLY
  • Tasmota's pages: webserver.BUTTON_CONFIGURATION, webserver.BUTTON_INFORMATION, webserver.BUTTON_MAIN, webserver.BUTTON_MANAGEMENT, webserver.BUTTON_MODULE
  • Methods received by handler: webserver.HTTP_ANY, webserver.HTTP_GET, webserver.HTTP_OPTIONS, webserver.HTTP_POST

See the Berry Cookbook for examples.

tcpclient class~

Simple tcp client supporting string and binary transfers:

  • create an instance of the client with var tcp = tcpclient()
  • connect to the server tcp.connect(address:string, port:int [, timeout_ms:int]) -> bool Address can be numerical IPv4 or domain name. Returns true if the connection succeeded. Optional timeout in milliseconds. The default timeout is USE_BERRY_WEBCLIENT_TIMEOUT (2 seconds).
  • check if the socket is connected with tcp.connected()
  • send content with tcp.write(content:string or bytes) -> int. Accepts either a string or a bytes buffer, returns the number of bytes sent. It's your responsibility to resend the missing bytes
  • check if bytes are available for reading tcp.available() -> int. Returns 0 if nothing was received. This is the call you should make in loops for polling.
  • read incoming content as string tcp.read() -> string or as bytes tcp.readbytes() -> bytes. It is best to call tcp.available() first to avoid creating empty response objects when not needed
  • close the socket with tcp.close()
tcpclient Function Parameters and details
connect connect(address:string, port:int [, timeout_ms:int]) -> bool
Connect to addr:port with optional timeout in milliseconds (default 2s).
Returns true if connection was successful, the call is blocking until the connection succeeded to the timeout expired.
connected connected() -> bool
Returns true if the connection was successful and is still valid (not dropped by server or closed by client)
close close() -> nil
Drops the current connection.
write write(content:string or bytes) -> int
Accepts either a string or a bytes buffer, returns the number of bytes sent. It's you responsibility to resend the missing bytes.
Returns 0 if something went wrong.
available available() -> int
Returns the number of bytes received in buffer and ready to be read.
read read([max_len:int]) -> string
Returns all the bytes received in Rx buffer as string.
Optional max_len parameter limits the number of characters returned, or read as much as possible by default.
readbytes read([max_bytes:int]) -> bytes()
Returns all the bytes received in Rx buffer as bytes().
Optional max_bytes parameter limits the number of bytes returned, or read as much as possible by default.

Full example:

tcp = tcpclient()
+tcp.connect("192.168.2.204", 80)
+print("connected:", tcp.connected())
+s= "GET / HTTP/1.0\r\n\r\n"
+tcp.write(s)
+print("available1:", tcp.available())
+tasmota.delay(100)
+print("available1:", tcp.available())
+r = tcp.read()
+tcp.close()
+print(r)
+

tcpclientasync class~

Variant of tcpclient using only non-blocking calls in full asynchronous mode. This allows to have multiple concurrent connections with fine-grained control over timeouts and no blocking of Tasmota. This is especially useful for Matter Border Router for ESP8266 Tasmota based devices via HTTP.

All calls return immediately, so you need to poll the API periodically to send/receive data, and manage timeouts yourself.

Typical equence:

  • create an instance of the client with var tcp = tcpclientasync()
  • connect to the server tcp.connect(address:string, port:int) -> bool. Address should be numerical IPv4 or IPv6 if you want the call to return immediately (i.e. do DNS resolution ahead of time), otherwise a DNS resolution might take some time and fail. If DNS failed, this call returns false.
  • regularly call connected() waiting for true to detect when the connection is established. While connected() returns nil then connection is in-progress. If connected() changes to false then the connection was refused by the host.
  • if the connection is not established after a definite amount of time, you should declare 'timeout' and call close()
  • to send data: first call listening() to ensure that the socket is ready to send data. Note: the socket is always listening when the connection was just established. Then call write() to send you data (string or bytes), this call returns the actual amount of data sent; if it is lower than your content, you need to handle yourself re-sending the remaining data. Note: ensuring that you send less than the MTU should keep you from happening (~1430 bytes max).
  • to receive data: first call available() to check if some data is ready to be received. Then call read() or readbytes() to get the buffer as string or bytes. You can limit the amount of data received, but in such case, the extra data is discarded and lost.
  • regularly call connected() to check if the connection is still up
  • finally call close() to close the connection on your side and free resources. It is implicitly called if the connection was closed from the peer.
tcpclient Function Parameters and details
connect connect(address:string, port:int) -> bool
Initiates a connection to addr:port.
If addr is in numerical format, DNS is immediate, and this calls returns immediately.
If addr is a domain name, the DNS resolution is made in blocking mode and call returns true if successful, or false if DNS failed.
Hence if you want a pure non-blocking mode, you should do the DNS resolution ahead of time.
connected connected() -> bool or nil
Returns:
nil if the connection in still on-going and was not yet established
true if the connection is established.
false if the connection is down, either because it was refused or closed (if it changed from true to false)
listening listening() -> bool
Returns true if the socket is ready to send data (hence established and out buffer empty), or false if the out buffer is not empty or the connection is down.
You should always wait for listening() to be true before calling write().
available available() -> int
Returns the number of bytes received in buffer and ready to be read, or 0 if nothing to read.
close close() -> nil
Close the current connection and free the file descriptor.
write write(content:string or bytes) -> int
Accepts either a string or a bytes buffer, returns the number of bytes sent. It's you responsibility to resend the missing bytes.
Returns 0 if something went wrong.
read read([max_len:int]) -> string
Returns all the bytes received in Rx buffer as string.
Optional max_len parameter limits the number of characters returned, or read as much as possible by default. However in the non-blocking version, limiting receive buffer will truncate (and lose) any extra data.
readbytes read([max_bytes:int]) -> bytes()
Returns all the bytes received in Rx buffer as bytes().
Optional max_bytes parameter limits the number of bytes returned, or read as much as possible by default. However in the non-blocking version, limiting receive buffer will truncate (and lose) any extra data.
info info() -> map
Returns a map with various information about the socket.
Example: {'listening': true, 'local_addr': '192.168.1.20', 'available': false, 'fd': 50, 'connected': true, 'local_port': 64808}
fd: the file descriptor number used internally
connected, listening, available: values of corresponding methods
local_addr, local_port: local address used and local port.

Full example:

def try_connect(addr, port)
+  import string
+  var tcp = tcpclientasync()
+  var now = tasmota.millis()
+  var r = tcp.connect(addr, port)
+  print(string.format("Time=%5i state=%s", tasmota.millis()-now, str(tcp.connected())))
+  print(tcp.info())
+  tasmota.delay(50)
+  print(string.format("Time=%5i state=%s", tasmota.millis()-now, str(tcp.connected())))
+  print(tcp.info())
+  tasmota.delay(150)
+  print(string.format("Time=%5i state=%s", tasmota.millis()-now, str(tcp.connected())))
+  print(tcp.info())
+  tasmota.delay(500)
+  print(string.format("Time=%5i state=%s", tasmota.millis()-now, str(tcp.connected())))
+  print(tcp.info())
+  return tcp
+end
+tcp = try_connect("192.168.1.19", 80)
+

tcpserver class~

Simple tcp server (socket) listening for incoming connection on any port.

  • create an instance of the tcpserver on a specific port with s = tcpserver(8888)
  • periodically call s.hasclient() to know if a new client has connected
  • if the previous returned true, call var c = s.accept() to accept the connection. It returns an instance of tcpclient as above; it responds to the same APIs as outgoing TCP connection and allows text and binary transfers.
  • you can call c.close() to close the connection, or call c.connected() to know if it's still connected (i.e. the client hasn't closed the connection on their side)
  • close the server with s.close(). This will prevent the server from receinving any new connection, but existing connections are kept alive.
tcpserver Function Parameters and details
constructor tcpserver(port:int) -> nit
Opens a socket on port and starts lisenting to new incoming connections. If the server can't open the socket (ex: it is already in use) an exception is raised
hasclient hasclient() -> bool
Returns true if a new client connected to the socket, in such case you should call accept(). You need to call this method regularly (ex: in event loop or fast_loop)
accept accept() -> instance:tcpclient or nil
Returns an instance of tcpclient for the new incoming connection, or raise an exception if no connection is available. You should call hasclient() returning true before calling accept().

Full example:

> s = tcpserver(8888)    # listen on port 8888
+> s.hasclient()
+false
+
+# in parallel connect on this port with `nc <ip_address> 8888`
+
+> s.hasclient()
+true               # we have an incoming connection
+> c = s.accept()
+> c
+<instance: tcpclient()>
+
+# send 'foobar' from the client
+> c.read()
+foobar
+
+# send 'foobar2' again from the client
+> c.readbytes()
+bytes('666F6F626172320A')
+
+> c.close()
+# this closes the connection
+

udp class~

Class udp provides ability to send and received UDP packets, including multicast addresses.

You need to create an object of class udp. Such object can send packets and listen to local ports. If you don't specify a local port, the client will take a random source port. Otherwise the local port is used as source port.

When creating a local port, you need to use udp->begin(<ip>, <port)>. If <ip> is empty string "" then the port is open on all interfaces (wifi and ethernet).

General Function Parameters and details
udp() udp() -> <instance udp>
Creates an instance of udp class.
begin begin(interface:string, port:int) -> bool
Create a UDP listener and sender on a specific interface (IP address) or on all interfaces if interface is an empty string
Listen on a specific port number, or set 0 to choose a random free port for sending only
Returns true if successful.
begin_multicast begin(ip:string, port:int) -> bool
Create a UDP listener and sender on interface ip and port. ip must be a multicast address.
Returns true if successful.
close close() -> bil
Closes UDP listener and sender, and frees resources. You can't send or receive anymore with this instance.
send send(addr:string, port:int, payload:bytes) -> bool
Sends a packet to address addr, port port and message as bytes() buffer.
Returns true if successful.
send_multicast send(payload:bytes) -> bool
Sends a payload as bytes() buffer to the multicast address. begin_multicast() must have been previously called.
Returns true if successful.
You can also send a multicast packet with send if you specify the multicast address and port.
read read() -> bytes() ornil<BR>Reads any received udp packet as bytes() buffer, ornil` if no packet was received.
remote_ip remote_ip (string or nil)
Instance variable containing the remote ip (as string) from the last successful read() command.
remote_port remote_port (int or nil)
Instance variable containing the remote port (as int) from the last successful read() command.

Sending udp packets~

> u = udp()
+> u.begin("", 2000)    # listen on all interfaces, port 2000
+true
+> u.send("192.168.1.10", 2000, bytes("414243"))   # send 'ABC' to 192.168.1.10:2000, source port is 2000
+true
+

Receive udp packets~

You need to do polling on udp->read(). If no packet was received, the call immediately returns nil.

> u = udp()
+> u.begin("", 2000)    # listen on all interfaces, port 2000
+true
+> u.read()     # if no packet received, returns `nil`
+>
+
+> u.read()     # if no packet received, returns `nil`
+bytes("414243")    # received packet as `bytes()`
+

Simple UDP server printing received packets~

class udp_listener
+  var u
+  def init(ip, port)
+    self.u = udp()
+    print(self.u.begin_multicast(ip, port))
+    tasmota.add_driver(self)
+  end
+  def every_50ms()
+    import string
+    var packet = self.u.read()
+    while packet != nil
+      tasmota.log(string.format(">>> Received packet ([%s]:%i): %s", self.u.remote_ip, self.u.remote_port, packet.tohex()), 2)
+      packet = self.u.read()
+    end
+  end
+end
+
+# listen on port 2000 for all interfaces
+# udp_listener("", 2000)
+

Send and receive multicast~

IPv4 example, using the udp_listener listener above.

On receiver side:

udp_listener("224.3.0.1", 2000)
+

On sender side:

u = udp()
+u.begin_multicast("224.3.0.1", 2000)
+u.send_multicast(bytes().fromstring("hello"))
+
+# alternatively
+u = udp()
+u.begin("", 0)      # send on all interfaces, choose random port number
+u.send("224.3.0.1", 2000, bytes().fromstring("world"))
+

The receiver will show:

>>> Received packet ([192.168.x.x]:2000): 68656C6C6F
+>>> Received packet ([192.168.x.x]:64882): 776F726C64
+

This works the same with IPv6 using an address like "FF35:0040:FD00::AABB"

mdns module~

Module import mdns support for mdns (Multicast DNS, aka Bonjour protocol) announces. This is needed for Matter Wifi support.

This feature requires #define USE_DISCOVERY compile option (not included in standard builds).

Example (announce of a Matter Wifi device):

import mdns
+mdns.start()
+mdns.add_service("_matterc","_udp", 5540, {"VP":"65521+32768", "SII":5000, "SAI":300, "T":1, "D":3840, "CM":1, "PH":33, "PI":""})
+
General Function Parameters and details
start mdns.start([hostname: string]) -> nil
Start or restart mdns, specify a new hostname if needed or implicitly use tasmota.hostname() if none provided (default)
stop mdns.stop() -> nil
Free all mdns resources
set_hostname mdsn.set_hostname(hostname:string) -> nil
Change the hostname
add_service mdns.add_service(service:string, proto:string, port:int, txt:map) -> nil
Add a service declaration using the current hostname as instance name, and specify TXT fields as a map

Addressable leds (WS2812, SK6812)~

There is native support for addressable leds via NeoPixelBus, with support for animations. Currently supported: WS2812, SK6812.

Details are in Berry leds

serial class~

The serial class provides a low-level interface to hardware UART. The serial GPIOs don't need to be configured in the template.

Example

# gpio_rx:4 gpio_tx:5
+ser = serial(4, 5, 9600, serial.SERIAL_7E1)
+
+ser.write(bytes(203132))   # send binary 203132
+ser.write(bytes().fromstring("Hello))   # send string "Hello"
+
+msg = ser.read()   # read bytes from serial as bytes
+print(msg.asstring())   # print the message as string
+
Tasmota Function Parameters and details
serial (constructor) serial(gpio_rx:int, gpio_tx:int, baud:int [, mode:int])
Creates a serial object
gpio_rx receive GPIO (or -1 if transmit only)
gpio_tx transmit GPIO (or -1 if receive only)
baud speed, ex: 9600, 115200
mode serial message format, default is serial.SERIAL_8N1 (8 bits, no parity, 1 stop bit).
Other mode values are described below.
write write(val:int || bytes()) -> bytes_sent:int
Send either a single byte if argument is int, or send a binary message from a bytes() object.
The methods blocks until all messages are sent to the UART hardware buffer; they may not all have been sent over the wire
read read(void) -> bytes()
Read all bytes received in the incoming buffer. If the buffer is empty, returns an empty bytes() object
flush flush(void) -> void
Flushes all buffers. Waits for all outgoing messages to be sent over the wire and clear the incoming buffer.
available available(void) -> int
Returns the number of incoming bytes in the incoming buffer, 0 in none.

Supported serial message formats: SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1, SERIAL_5N2, SERIAL_6N2, SERIAL_7N2, SERIAL_8N2, SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_8E1, SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, SERIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1, SERIAL_5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2

display module~

The display module provides a simple API to initialize the Universal Display Driver with data provided as a string. It is used by autoconf mechanism.

Tasmota Function Parameters and details
start display.start(displayini:string) -> nil
Initializes the Universal Display Driver with the string provided as argument, similar to content in display.ini. It is typically read from a file in the file-system.
started display.started() -> bool
Returns true if display is already initialized, false if not started.
dimmer display.started([dim:int]) -> int
Sets the dimmer of display, value 0..100. If 0 then turn off display. If no arg, read the current value.
driver_name display.driver_name() -> string
Returns the Display driver name as specified in display.ini
touch_update display.touch_update(touches:int, raw_x:int, raw_y:int, gesture:int) -> nil
Sets the last Touch Screen update values to be passed to LVGL. This allows an external touchscreen driver to periodically update the touch information.
touches: number of touches (0 = no touch, 1 = screen touched). Multiple touch is not supported
raw_x and raw_y = coordinates before conversion (resistive touch screens need conversion)
gesture: type of gesture. 0 = no gesture, 16 = move up, 17 = move down, 18 = move left, 19 = move right, 32 = zoom in, 33 = zoom out.

uuid module~

The uuid module allows to generate uuid4 random ids.

> import uuid
+> uuid.uuid4()
+1a8b7f78-59d8-4868-96a7-b7ff3477d43f
+
Tasmota Function Parameters and details
uuid4 uuid.uuid4() -> string
Generates a uuid4 random id as string.

crc module~

The crc module allows to compute crc32/16/8 from bytes() arrays.

> import crc
+> crc.crc32(0xFFFFFFFF, bytes("AABBCC"))
+-1091314015
+> crc.crc16(0xFFFF, bytes("AABBCC"))
+20980
+> crc.crc8(0xFF, bytes("AABBCC"))
+139
+
Tasmota Function Parameters and details
crc32 crc.crc32(crc:int, payload:bytes) -> int
Compute crc32 from an initial value and a bytes() buffer
crc16 crc.crc16(crc:int, payload:bytes) -> int
Compute crc16 from an initial value and a bytes() buffer
crc8 crc.crc8(crc:int, payload:bytes) -> int
Compute crc8 from an initial value and a bytes() buffer

tasmota_log_reader class~

The tasmota_log_reader class allows you to read and potentially parse the Tasmota logs. It keeps track of what logs were already read in the past and feeds you with new log lines if some are available. It is for example used by the LVGL tasmota_log widget to display logs on a display.

Note: calling tasmota_log_reader can be expensive in string allocations, and adds pressure on the garbage collector. Use wisely.

Example:

var lr = tasmota_log_reader()
+
+# do this regularly
+var ret = lr.get_log(2)    # read at log level 2
+if ret != nil
+  var lines = r.split('\n')  # extract as a list of lines
+  # do whatever you need
+end
+
Tasmota Function Parameters and details
tasmota_log_reader() tasmota_log_reader(void) -> instance(tasmota_log_reader)
Instantiate a new tasmota_log_reader. Multiple readers can coexist and they each keep track of already read log lines
get_log get_log(log_level:int) -> string or nil
Returns new log lines as a big string object. Lines are separated by \n. Returns nil if no new logs are available.
log_level can be 0..4 and specifies the highest log level that we be reported (it is usually wise to start with 2). Higher log level will be reported only if they are recorded, i.e. there is at least one logger that asks for it. This class does not cause log-level 4 to be recorded if none other loggers are recording them (weblog, mqttlog or seriallog).

ULP module~

The ULP module exposes the third computing unit of the ESP32, which is a simple finite state machine (FSM) that is designed to perform measurements using the ADC, temperature sensor and even external I2C sensors. This small ultra low power coprocessor can run in parallel to the main cores and in deep sleep mode, where it is capable to wake up the system, i.e. in reaction to sensor measurements. The binding to Berry consists of some lightweight wrapper functions and the communication with the main cores works by accessing the RTC_SLOW_MEM from both sides, which is the same way as in any other ESP32 ULP project.

# simple LED blink example
+import ULP
+ULP.wake_period(0,500000) # off time
+ULP.wake_period(1,200000) # on time 
+c = bytes("756c70000c006c00000000001000008000000000000000000000000010008072010000d0e5af2c72340040802705cc190005681d10008072e1af8c720100006821008072040000d0120080720800207004000068010005825c0000800405681d00000092680000800505681d0100009268000080000000b0")
+ULP.load(c)
+ULP.run()
+
Tasmota Function Parameters and details
run ULP.run() -> nil
Execute ULP prgramm
load ULP.load(code:bytes) -> nil
Load ULP code from a bytes() buffer into memory
set_mem ULP.set_mem(addr:int, value:int) -> nil
Set memory position in RTC_SLOW_MEM to value. Address and Value are 32-bit!!
get_mem ULP.set_mem(addr:int) -> int16_t
Get value from memory position in RTC_SLOW_MEM. By hardware design only the lower 16-bit are usable, so this function already masks out the upper 16-bit
gpio_init ULP.gpio_init(pin:int, mode:int) -> pin:int
Makes a valid GPIO pin accessible to the ULP and sets the mode according to the enum 'rtc_gpio_mode_t', returns the same pin, but translated to the RTC system, which is the numbering scheme in the assembly code
adc_config ULP.adc_config(channel:int, attenuation:int, width:int) -> nil
Configures ADC pin usage for the ULP according to the enums ' adc1_channel_t', 'adc_atten_t' and 'adc_bits_width_t'
wake_period ULP.wake_period(register:int, time:int) -> nil
Configures one of 5 (0..4) wake timer registers with the time value in microseconds
sleep ULP.wake_period([time:int]) -> nil
Starts deep sleep mode and allow wake up by the ULP, with an optional time value in seconds an additional wake up timer gets started

More infos (including suggestions for a toolchain) on the ULP page.

re regex module~

Use with import re.

There are two ways to use regex, first is to call directly the module which triggers a compilation of the regex at each call. The second one is to pre-compile the regex once into an object which is much more efficient if you need to use the regex multiple times. Any error in the compilation of the regex pattern yields an exception.

> import re
+
+# first series are all-in-one, patterns are compiled on the fly
+
+> re.search("a.*?b(z+)", "zaaaabbbccbbzzzee")
+['aaaabbbccbbzzz', 'zzz']
+> re.match("a.*?b(z+)", "aaaabbbccbbzzzee")
+['aaaabbbccbbzzz', 'zzz']
+> re.split('/', "foo/bar//baz")
+['foo', 'bar', '', 'baz']
+> re.searchall('<([a-zA-Z]+)>', '<abc> yeah <xyz>')
+[['<abc>', 'abc'], ['<xyz>', 'xyz']]
+
+# below are pre-compiled patterns, which is much faster if you use the
+# pattern multiple times
+
+> rr = re.compile('<([a-zA-Z]+)>')
+> rr.searchall('<abc> yeah <xyz>')
+[['<abc>', 'abc'], ['<xyz>', 'xyz']]
+
+> rr = re.compile("/")
+> rr
+<instance: re_pattern()>
+
+> rr.split("foo/bar//baz")
+['foo', 'bar', '', 'baz']
+> rr.split("/b")
+['', 'b']
+
Tasmota Function Parameters and details
search re.search(pattern:string, payload:string) -> list of strings
Returns the list of matches, or empty list of no match
match re.match(pattern:string, payload:string) -> list of strings
Returns the list of matches, or empty list of no match. The difference with search is that match must match from the beginning of the string.
searchall re.searchall(pattern:string, payload:string [, limit:string]) -> list of list of strings
Returns the list of list of matches, or empty list of no match. limit allows to limit the number of matches.
matchall re.matchall(pattern:string, payload:string [, limit:string]) -> list of list of strings
Returns the list of matches, or empty list of no match. The difference with searchall is that there should not be any gaps between matches. limit allows to limit the number of matches.
split re.search(pattern:string, payload:string) -> list of strings
Returns the list of strings from split, or a list with a single element containing the entire string if no match
compile re.compile(pattern:string) -> instance of <re_pattern>
Compiles the regex into a reusable faster bytecode. You can then call the following methods:
search(), match(), split() similarly to the module's functions.

Note: for match and search, the first element in the list contains the global match of the pattern. Additional elements correspond to the sub-groups (in parenthesis).

The regex engine is based on re1.5 also used in Micropython.

crypto module~

Module import crypto support for common cryptographic algorithms.

Currently supported algorithms:

  • AES CTR 256 bits - requires #define USE_BERRY_CRYPTO_AES_CTR
  • AES GCM 256 bits
  • AES CCM 128 or 256 bits
  • Elliptic Curve C25519 - requires #define USE_BERRY_CRYPTO_EC_C25519
  • Elliptic Curve P256 (secp256r1) - requires #define USE_BERRY_CRYPTO_EC_P256
  • HKDF key derivation with HMAC SHA256 - requires #define USE_BERRY_CRYPTO_HKDF_SHA256
  • HMAC SHA256
  • MD5
  • PKKDF2 with HMAC SHA256 key derivation - requires #define USE_BERRY_CRYPTO_PBKDF2_HMAC_SHA256
  • SHA256
  • JWT RS256 (RSASSA-PKCS1-v1_5 with SHA256) - requires #define USE_BERRY_CRYPTO_RSA

crypto.AES_CTR class~

Encrypt and decrypt, using AES CTR (Counter mode) with 256 bits keys.

General Function Parameters and details
init AES_CTR.init(secret_key:bytes(32)) -> instance
Initialise AES CTR instance with secret_key (256 bits) and iv (initialization vector or nonce, 96 bits)
encrypt encrypt(ciphertext:bytes, iv:bytes(12), cc:int) -> bytes
Encrypt the ciphertext. The iv (Initialization Vector) must be 12 bytes, it can be the concatenation of 4 bytes Nonce and 8 bytes iv. cc is the counter (4 bytes) incremented for each block of 16 bytes.
Note: the last counter value is not returned, so it is advised to encrypt all data at once.
decrypt decrypt(ciphertext:bytes, iv:bytes(12), cc:int) -> bytes
Identical to encrypt above.

Test vectors from https://datatracker.ietf.org/doc/html/rfc4231

# Test case from https://www.ietf.org/rfc/rfc3686.txt
+import crypto
+key = bytes("F6D66D6BD52D59BB0796365879EFF886C66DD51A5B6A99744B50590C87A23884")
+iv = bytes("00FAAC24C1585EF15A43D875")
+cc = 0x000001
+aes = crypto.AES_CTR(key)
+plain = bytes("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")
+cipher = aes.encrypt(plain, iv, cc)
+assert(cipher == bytes("F05E231B3894612C49EE000B804EB2A9B8306B508F839D6A5530831D9344AF1C"))
+plain2 = aes.decrypt(cipher, iv, cc)
+assert(plain == plain2)
+

crypto.AES_GCM class~

Encrypt, decrypt and verify, using AES GCM (Gallois Counter Mode) with 256 bits keys.

General Function Parameters and details
init AES_GCM.init(secret_key:bytes(32), iv:bytes(12)) -> instance
Initialise AES GCM instance with secret_key (256 bits) and iv (initialization vector or nonce, 96 bits)
encrypt encrypt(ciphertext:bytes) -> bytes
Encrypt the ciphertext. Can be called multiple times, the tag is updated accordingly
decrypt decrypt(ciphertext:bytes) -> bytes
Decrypt the ciphertext. Can be called multiple times, the tag is updated accordingly
tag tag() -> bytes
Compute the verification tag for the object encrypted or decrypted (128 bits).

Example taken from https://wizardforcel.gitbooks.io/practical-cryptography-for-developers-book/content/symmetric-key-ciphers/aes-encrypt-decrypt-examples.html

import crypto
+
+key = bytes('233f8ce4ac6aa125927ccd98af5750d08c9c61d98a3f5d43cbf096b4caaebe80')
+ciphertext = bytes('1334cd5d487f7f47924187c94424a2079656838e063e5521e7779e441aa513de268550a89917fbfb0492fc')
+iv = bytes('2f3849399c60cb04b923bd33265b81c7')
+authTag = bytes('af453a410d142bc6f926c0f3bc776390')
+
+# decrypt ciphertext with key and iv
+aes = crypto.AES_GCM(key, iv)
+plaintext = aes.decrypt(ciphertext)
+print(plaintext.asstring())
+# 'Message for AES-256-GCM + Scrypt encryption'
+
+tag = aes.tag()
+print(tag == authTag)
+# true
+

crypto.AES_CCM class~

Encrypt and decrypt, using AES CCM with 256 bits keys.

General Function Parameters and details
init AES_CCM.init(secret_key:bytes(16 or 32), iv:bytes(7..13), aad:bytes(), data_len:int, tag_len:int) -> instance
Initialise AES CCM instance with secret_key (128 or 256 bits), iv (initialization vector or nonce, 56 to 104 bits), aad is the associated data, data_len is the size of the payload that you need to announce in advance, tag_len is the lenght in bytes of the tag (normally 16).
encrypt encrypt(ciphertext:bytes) -> bytes
Encrypt the ciphertext.
decrypt decrypt(ciphertext:bytes) -> bytes
Identical to encrypt above.
tag tag() -> bytes
Returns the tag or MIC.
decrypt1 AES_CCM.decrypt1(secret_key:bytes(16 or 32), iv:bytes(), iv_start:int, iv_len:int (7..13), aad:bytes(), aad_start:int, aad_len:int, data:bytes(), data_start:int, data_len:int, tag:bytes(), tag_start:int, tag_len:int (4..16)) -> bool (true if tag matches)
Decrypt in a single call, avoiding any object allocation
encrypt1 AES_CCM.encrypt1(secret_key:bytes(16 or 32), iv:bytes(), iv_start:int, iv_len:int (7..13), aad:bytes(), aad_start:int, aad_len:int, data:bytes(), data_start:int, data_len:int, tag:bytes(), tag_start:int, tag_len:int (4..16)) -> bool (always true)
Decrypt in a single call, avoiding any object allocation. Data is encrypted in-place and Tag is changed in the buffer.

Example from Matter:

# raw_in is the received frame
+raw_in = bytes("00A0DE009A5E3D0F3E85246C0EB1AA630A99042B82EC903483E26A4148C8AC909B12EF8CDB6B144493ABD6278EDBA8859C9B2C")
+
+payload_idx = 8     # unencrypted header is 8 bytes
+tag_len = 16        # MIC is 16 bytes
+
+p = raw[payload_idx .. -tag_len - 1]   # payload
+mic = raw[-tag_len .. ]                # MIC
+a = raw[0 .. payload_idx - 1]          # AAD
+
+i2r = bytes("92027B9F0DBC82491D4C3B3AFA5F2DEB")   # key
+# p   = bytes("3E85246C0EB1AA630A99042B82EC903483E26A4148C8AC909B12EF")
+# a     = bytes("00A0DE009A5E3D0F")
+n   = bytes("009A5E3D0F0000000000000000")         # nonce / IV
+# mic = bytes("8CDB6B144493ABD6278EDBA8859C9B2C")
+
+# expected cleartext
+clr = bytes("05024FF601001536001724020024031D2404031818290324FF0118")
+
+# method 1 - with distinct calls
+import crypto
+aes = crypto.AES_CCM(i2r, n, a, size(p), 16)
+cleartext = aes.decrypt(p)
+tag = aes.tag()
+
+assert(cleartext == clr)
+assert(tag == mic)
+
+# method 2 - single call
+raw = raw_in.copy()      # copy first if we want to keep the encrypted version
+var ret = crypto.AES_CCM.decrypt1(i2r, n, 0, size(n), raw, 0, payload_idx, raw, payload_idx, size(raw) - payload_idx - tag_len, raw, size(raw) - tag_len, tag_len)
+
+assert(ret)
+assert(raw[payload_idx .. -tag_len - 1] == clr)
+

crypto.EC_C25519 class~

Provides Elliptic Curve C25519 Diffie-Hellman key agreement. Requires #define USE_BERRY_CRYPTO_EC_C25519

General Function Parameters and details
public_key crypto.EC_C25519().public_key(secret_key:bytes(32)) -> bytes(32)
Computes the public key given a random private key.
shared_key crypto.EC_C25519().shared_key(our_private_key:bytes(32), their_public_key:bytes(32)) -> bytes(32)
Compute a shared key (Diffie-Hellman) using our private key and the other party's public key. The other party will compute the same shared key using their private key and our pubic key.

Example from test vectors https://www.rfc-editor.org/rfc/rfc7748:

import crypto
+
+# alice side
+alice_priv_key = bytes("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")
+alice_pub_key = bytes("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")
+assert(crypto.EC_C25519().public_key(alice_priv_key) == alice_pub_key)
+
+# bob side
+bob_priv_key = bytes("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb")
+bob_pub_key = bytes("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f")
+assert(crypto.EC_C25519().public_key(bob_priv_key) == bob_pub_key)
+
+# shared key computed by alice
+ref_shared_key = bytes("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742")
+alice_shared_key = crypto.EC_C25519().shared_key(alice_priv_key, bob_pub_key)
+bob_shared_key = crypto.EC_C25519().shared_key(bob_priv_key, alice_pub_key)
+assert(alice_shared_key == ref_shared_key)
+assert(bob_shared_key == ref_shared_key)
+

crypto.EC_P256 class~

Provides Elliptic Curve Prime256 (secp256r1) Diffie-Hellman key agreement and various functions on P256 curve. Requires #define USE_BERRY_CRYPTO_EC_P256

General Function Parameters and details
public_key crypto.EC_P256().public_key(secret_key:bytes(32)) -> bytes(65)
Computes the public key given a random private key. The result is uncompressed point coordinates starting with 0x04 (65 bytes in total)
shared_key crypto.EC_P256().shared_key(our_private_key:bytes(32), their_public_key:bytes(65)) -> bytes(32)
Compute a shared key (Diffie-Hellman) using our private key and the other party's public key. The other party will compute the same shared key using their private key and our pubic key.
The result is actually the X coordinate of the multiplication of the points coordinates of the public key, and a large number (private key)
Specific Functions Parameters and details
mod crypto.EC_P256().mod(data:bytes()) -> bytes(32)
Computes the modulus of an arbitrary large number. The modulus is done towards the order of the curve.
neg crypto.EC_P256().neg(data:bytes(32)) -> bytes(32)
-x mod p or p - x if x is lower than p
Computes the opposite (negate) of a number modulus the order of the curve (it's actuall modulus - data).
mul crypto.EC_P256().mul(x:bytes(), A:bytes(65)) -> bytes(65)
x * A
Computes multiplication of a number and a point on the curve.
x needs to be smaller than p, use mod() if not sure
The function checks that the point A is on the curve, or raises an error
muladd crypto.EC_P256().muladd(x:bytes(), A:bytes(65), y:bytes(), B:bytes(65)) -> bytes(65)
x * A + y * B
x and y need to be smaller than p, use mod() if not sure
The function checks that the points A and B are on the curve, or raises an error
If B is empty bytes(), the Generator P of the curve is used instead.

Example:

import crypto
+priv = bytes("f502fb911d746b77f4438c674e1c43650b68285dfcc0583c49cd6ed88f0fbb58")
+p = crypto.EC_P256()
+pub = p.public_key(priv)
+assert(pub == bytes("04F94C20D682DA29B7E99985D8DBA6ABEA9051D16508742899835098B1113D3D749466644C47B559DB184556C1733C33E5788AE250B8FB45F29D4CF48FF752C1ED"))
+
+import crypto
+priv = bytes("4E832960415F2B5FA2B1FDA75C1A8F3C84BAEB189EDC47211EF6D27A21FC0ED8")
+p = crypto.EC_P256()
+pub = p.public_key(priv)
+assert(pub == bytes("042166AE4F89981472B7589B8D79B8F1244E2EEE6E0A737FFBFED2981DA3E193D6643317E054D2A924F2F56F1BF4BECA13192B27D8566AF379FBBF8615A223D899"))
+print("x=",pub[1..32])
+print("y=",pub[33..65])
+
+import crypto
+p = crypto.EC_P256()
+priv_A = bytes("f502fb911d746b77f4438c674e1c43650b68285dfcc0583c49cd6ed88f0fbb58")
+pub_A = bytes("04F94C20D682DA29B7E99985D8DBA6ABEA9051D16508742899835098B1113D3D749466644C47B559DB184556C1733C33E5788AE250B8FB45F29D4CF48FF752C1ED")
+priv_B = bytes("4E832960415F2B5FA2B1FDA75C1A8F3C84BAEB189EDC47211EF6D27A21FC0ED8")
+pub_B = bytes("042166AE4F89981472B7589B8D79B8F1244E2EEE6E0A737FFBFED2981DA3E193D6643317E054D2A924F2F56F1BF4BECA13192B27D8566AF379FBBF8615A223D899")
+
+shared_1 = p.shared_key(priv_A, pub_B)
+shared_2 = p.shared_key(priv_B, pub_A)
+assert(shared_1 == shared_2)
+

crypto.HKDF_SHA256 class~

Provides HKDF using HMAC SHA256 key derivation. Turns 'ikm' (input keying material) of low entropy and creates a pseudo random key. Requires #define USE_BERRY_CRYPTO_HKDF_SHA256

General Function Parameters and details
derive crypto.HKDF_SHA256().derive(ikm:bytes(), salt:bytes(), info:bytes(), out_bytes:int) -> bytes(out_bytes)
Computes a key derivation function
ikm is the input keying material, typically a password
salt can be empty
info can be empty and is used to create multiple derived keys
out_bytes indicates the number of bytes to generate (between 1 and 256)

Test vectors from https://www.rfc-editor.org/rfc/rfc5869

import crypto
+
+# Test Case 1
+hk = crypto.HKDF_SHA256()
+ikm = bytes("0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B")
+salt = bytes("000102030405060708090A0B0C")
+info = bytes("F0F1F2F3F4F5F6F7F8F9")
+k = hk.derive(ikm, salt, info, 42)
+assert(k == bytes("3CB25F25FAACD57A90434F64D0362F2A2D2D0A90CF1A5A4C5DB02D56ECC4C5BF34007208D5B887185865"))
+
+# Test Case 2
+hk = crypto.HKDF_SHA256()
+ikm  = bytes("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f")
+salt = bytes("606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf")
+info = bytes("b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")
+k = hk.derive(ikm, salt, info, 82)
+assert(k == bytes("b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87"))
+
+# Test Case 3
+hk = crypto.HKDF_SHA256()
+ikm  = bytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")
+salt = bytes()
+info = bytes()
+k = hk.derive(ikm, salt, info, 42)
+assert(k == bytes("8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8"))
+

crypto.PBKDF2_HMAC_SHA256 class~

Provides PBKDF2 using HMAC SHA256 key derivation. Turns a password into a hash.

General Function Parameters and details
derive crypto.PBKDF2_HMAC_SHA256().derive(password:bytes(), salt:bytes(), iterations:int, out_bytes:int) -> bytes(out_bytes)
Computes a key derivation function
password is the input keying material
salt can be empty bytes()
iterations counts the number of iterations of HMAC, limited to 10000 to make computation short enough for ESP32
out_bytes indicates the number of bytes to generate (between 1 and 256)

Test vectors from https://github.com/brycx/Test-Vector-Generation/blob/master/PBKDF2/pbkdf2-hmac-sha2-test-vectors.md

import crypto
+pb = crypto.PBKDF2_HMAC_SHA256()
+
+assert(pb.derive("password", "salt", 1, 20) == bytes('120fb6cffcf8b32c43e7225256c4f837a86548c9'))
+
+assert(pb.derive("password", "salt", 2, 20) == bytes('ae4d0c95af6b46d32d0adff928f06dd02a303f8e'))
+
+assert(pb.derive("password", "salt", 3, 20) == bytes('ad35240ac683febfaf3cd49d845473fbbbaa2437'))
+
+assert(pb.derive("password", "salt", 4096, 20) == bytes('c5e478d59288c841aa530db6845c4c8d962893a0'))
+
+assert(pb.derive("passwd", "salt", 1, 128) == bytes('55AC046E56E3089FEC1691C22544B605F94185216DDE0465E68B9D57C20DACBC49CA9CCCF179B645991664B39D77EF317C71B845B1E30BD509112041D3A19783C294E850150390E1160C34D62E9665D659AE49D314510FC98274CC79681968104B8F89237E69B2D549111868658BE62F59BD715CAC44A1147ED5317C9BAE6B2A'))
+

crypto.SHA256 class~

Provides SHA256 hashing function

General Function Parameters and details
init HMAC_SHA256.init() -> instance
Initialise SHA256 hashing function
update update(data:bytes) -> self
Add content to the hash. Calls can be chained.
out out() -> bytes(32)
Output the value of the hash

Example test vectors from https://www.dlitz.net/crypto/shad256-test-vectors/

import crypto
+h = crypto.SHA256()
+
+# SHA256 of empty message
+assert(h.out() == bytes("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"))
+
+# (first 16 bytes of RC4 keystream where the key = 0)
+h.update(bytes("de188941a3375d3a8a061e67576e926d"))
+assert(h.out() == bytes("067c531269735ca7f541fdaca8f0dc76305d3cada140f89372a410fe5eff6e4d"))
+

crypto.HMAC_SHA256 class~

Provides HMAC SHA256 hashing function

General Function Parameters and details
init HMAC_SHA256.init(key:bytes) -> instance
Initialise HMAC_SHA256 hashing function with a provided key
update update(data:bytes) -> self
Add content to the hash. Calls can be chained
out out() -> bytes(32)
Output the value of the hash

Test case from https://datatracker.ietf.org/doc/html/rfc4231:

import crypto
+key = bytes("4a656665")
+msg = bytes("7768617420646f2079612077616e7420666f72206e6f7468696e673f")
+h = crypto.HMAC_SHA256(key)
+h.update(msg)
+hmac = h.out()
+assert(hmac == bytes("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"))
+

crypto.RSA class~

Provides RSA core features, currently only JWT RS256 signing (RSASSA-PKCS1-v1_5 with SHA256) - requires #define USE_BERRY_CRYPTO_RSA

Function Parameters and details
rs256 crypto.RSA.rs256HMAC_SHA256.init(private_key:bytes(), payload:bytes()) -> bytes()
Sign a payload with an RSA private key in DER binary format.
private_key: (bytes) contains the binary DER (ASN.1) private key, see example below to convert from PEM.
payload (bytes) JWT payload to sign, it should be derived from JSON encoded as base64url
Outputs a bytes() array of the payload, hashed with SHA256 and signed with the RSA private key. The output is 256 bytes longs for a 2048 RSA key.

Signing a full JWT token with RS256

import string
+import crypto
+
+# JWT requires base64url and not raw base64
+# see https://base64.guru/standards/base64url
+# input: string or bytes
+def base64url(v)
+  import string
+  if type(v) == 'string'   v = bytes().fromstring(v) end
+  var b64 = v.tob64()
+  # remove trailing padding
+  b64 = string.tr(b64, '=', '')
+  b64 = string.tr(b64, '+', '-')
+  b64 = string.tr(b64, '/', '_')
+  return b64
+end
+
+# JWT header and claim
+var header = '{"alg":"RS256","typ":"JWT"}'
+var claim = '{"sub":"1234567890","name":"John Doe","admin":true,"iat":1516239022}'
+var b64header = base64url(header)
+var b64claim = base64url(claim)
+
+assert(b64header == 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9')
+assert(b64claim == 'eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0')
+
+# `body` is the payload to sign with RS256
+var body = b64header + '.' + b64claim
+assert(body == 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0')
+
+var private_key =
+'-----BEGIN PRIVATE KEY-----\n'+
+'MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj\n'+
+'MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu\n'+
+'NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ\n'+
+'qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg\n'+
+'p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR\n'+
+'ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi\n'+
+'VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV\n'+
+'laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8\n'+
+'sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H\n'+
+'mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY\n'+
+'dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw\n'+
+'ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ\n'+
+'DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T\n'+
+'N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t\n'+
+'0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv\n'+
+'t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU\n'+
+'AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk\n'+
+'48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL\n'+
+'DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK\n'+
+'xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA\n'+
+'mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh\n'+
+'2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz\n'+
+'et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr\n'+
+'VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD\n'+
+'TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc\n'+
+'dn/RsYEONbwQSjIfMPkvxF+8HQ==\n'+
+'-----END PRIVATE KEY-----\n'
+
+# public_key for reference but not actually used here
+var public_key =
+'-----BEGIN PUBLIC KEY-----\n'+
+'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo\n'+
+'4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u\n'+
+'+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh\n'+
+'kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ\n'+
+'0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg\n'+
+'cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc\n'+
+'mwIDAQAB\n'+
+'-----END PUBLIC KEY-----\n'
+
+# read private_key as DER binary
+while (private_key[-1] == '\n') private_key = private_key[0..-2] end
+var private_key_DER = bytes().fromb64(string.split(private_key, '\n')[1..-2].concat())
+
+# comparison with what was expected
+assert(private_key_DER.tob64() == 'MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvuNMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZqgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulgp2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlRZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwiVuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskVlaAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83HmQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwYdgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cwta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQDM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2TN0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPvt8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDUAhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISLDY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnKxt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEAmNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfzet6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhrVBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicDTQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cncdn/RsYEONbwQSjIfMPkvxF+8HQ==')
+
+# sign body
+var body_b64 = bytes().fromstring(body)
+var sign = crypto.RSA.rs256(private_key_DER, body_b64)
+var b64sign = base64url(sign)
+
+# check output
+assert(b64sign == 'NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ')
+
+# Final token:
+var jwt_token = payload + '.' + b64sign
+

crypto.MD5 class~

Provides MD5 hashing function.

General Function Parameters and details
init MD5.init() -> instance
Initialise MD5 hashing function
update update(data:bytes) -> self
Add content to the hash. Calls can be chained.
finish finish() -> bytes(16)
Finish the MD5 calculation and output the result (16 bytes)

Test vector:

import crypto
+h = crypto.MD5()
+t = bytes().fromstring("The quick brown fox jumps over the lazy dog")
+h.update(t)
+m = h.finish()
+assert(m == bytes("9e107d9d372bb6826bd81d3542a419d6"))
+

Philips Hue emulation for Alexa~

Berry extends the native Hue/Alexa emulation and makes it possible to handle any number of virtual lights. You can easily define "virtual" lights in Berry, respond to commands from Alexa and send light status.

It is up to you to define the final behavoir. For example you could control some fancy devices, light strips or whatever takes on/off, dimmer or RGB commands. Your imagination is the limit.

Hue emulation requires both #define USE_EMULATION and #define USE_EMULATION_HUE. Emulation must also be enabled with Emulation 2 command.

light_state class~

The core class is light_state which represents a virtual light.

light_state general methods:

Methods Parameters and details
init light_state.init(channels:int) -> instance
Creates a light_state instance for a light with channels channels.
Constants are:
light_state.RELAY = 0
light_state.DIMMER = 1
light_state.CT = 2
light_state.RGB = 3
light_state.RGBW = 4
light_state.RGBCT = 5
signal_change signal_change() -> nil
Called when a changed was triggered by Alexa.
You can sub-class this class and override this method. Alternatively you can also poll for any change.

light_state getters:

Attributes Parameters and details
power (bool) on/off state
reachable (bool) light is reachable
type (int) number of channels of the light
bri (int) brightness of the light (0..255)
ct (int) white temperature of the light (153..500)
sat (int) saturation of the light (0..255)
hue (int) hue of the light (0..360)
hue16 (int) hue as 16 bits (0..65535)
r
g
b
(int) Red Green Blue channels (0..255)
x
y
(float) x/y color as floats (0.0 .. 1.0)
mode_ct
mode_rgb
(bool) light is in RGB or CT mode
get get() -> map returns the complete state of the light as a map
Exemple:
{'rgb': '1E285A', 'hue': 230, 'type': 5, 'power': false, 'bri': 90, 'mode_rgb': true, 'sat': 170, 'mode_ct': false, 'channels': [30, 40, 90, 0, 0]}

light_state setters:

Methods Parameters and details
set_power set_power(bool) -> nil sets on/off state
set_reachable set_reachable(bool) -> nil sets the reachable state
set_bri set_bri(int) -> nil sets the brightness (0..255)
set_ct set_ct(int) -> nil sets the white temperature (153..500)
set_sat set_sat(int) -> nil sets the saturation (0..255)
set_huesat set_huesat(hue:int, sat:int) -> nil sets hue and saturation (0..360, 0..255)
set_hue16sat set_hue16sat(hue16:int, sat:int) -> nil sets hue16 and saturation (0..65535, 0..255)
set_rgb set_rgb(r:int, g:int, b=int) -> nil sets red/green/blue channels (0..255 x 3)
set_xy set_xy(x:float, y:float) -> nil sets color as x/y (0.0 .. 1.0 x 2)

light_state static helper functions:

Methods Parameters and details
gamma8 gamma8(int) -> nil applies gamma correction to 8 bits value (0..255)
gamma10 gamma10(int) -> nil applies gamma correction to 10 bits value (0..1023)
reverse_gamma10 reverse_gamma10(int) -> nil applies reverse gamma correction to 10 bits value (0..1023)

hue_bridge module~

Use import hue_bridge and declare all the virtual lights. Example:

# put this in `autoexec.be`
+import hue_bridge
+
+l1 = light_state(light_state.DIMMER)
+hue_bridge.add_light(11, l1, "Synthetic Dimmer", "V1", "Tasmota Factory")
+
+l2 = light_state(light_state.CT)
+hue_bridge.add_light(12, l2, "Synthetic CT", "V1", "Tasmota Factory")
+
+l5 = light_state(light_state.RGBCT)
+hue_bridge.add_light(15, l5, "Synthetic RGBCT")
+

When you start the Hue pairing, all virtual lights are advertized. You need to make sure that virtual lights are defined at each restart (in autoexec.be for example).

hue_bridge functions:

Methods Parameters and details
add_light add_light(id:int, light:instance of light_state, name:string [, model:string, manuf:strin]) -> light
Adds an virtual light to the Hue bridge.
id = numerical identifier of the Hue light. Using low numbers avoids conflict with real lights from Tasmota
light = instance of light_state handling the state and behavior of the light
name = name of the light as displayed in the Alexa app (can be overriden in the app)
model (opt) = name of the manufacturer model, defaults to "Unkwnon"
manuf (opt) = name of the manufacturer, defaults to "Tasmota"
remove_light remove_light(id:int) -> nil
Removes a light from the Hue bridge by hue id.
light_to_id light_to_id(light:instance) -> int converts a registered light_instance instance to its Hue id

Zigbee~

For Zigbee coordinators, there is a Berry mapping that allows explore Zigbee configurations and devices. It also allows to intercept incoming message (low and high level) and transform messages before they reach the Tasmota layer. This is useful for non-standard Zigbee devices for which Zigbee plug-ins are not sufficient.

Note: the following are only available when compiling with #define USE_ZIGBEE

Internally, the Tasmota Zigbee engine calls callBerryZigbeeDispatcher() at key points to allow your Berry code to take over and change messages on-the-fly.

import zigbee~

First step is to use import zigbee which returns an instance (monad) of zb_coord().

General methods Parameters and details
info zigbee.info() -> map returns a map with general configuration of the Zigbee coordinator.
Format is identical to ZbConfig
Example:
{'ext_pan_id': '0xCCCCCCCCA11A2233', 'tx_radio': 20, 'shortaddr': 0, 'longaddr': '0x00124B0026BAABBC', 'channel': 11, 'pan_id': 837, 'pan_id_hex': '0x0345', 'shortaddr_hex': '0x0000'}
size zigbee.size() -> int returns the number of devices knwon by the coordinator
iter zigbee.iter() -> iterator
Returns an iterator on all zigbee devices
Use compact implicit form:
for ze: zigbee print(ze) end
item
[]
zigbee.item(shortaddr:int) -> instance of zb_device
Returns the Zigbee device with short address shortaddr
You can use the compact syntax zigbee[0xFAB6]
abort zigbee.abort() -> nil aborts the initialization of Zigbee MCU. To be used when initialization of Zigbee failed

zb_device class~

The class zb_device contains all known information about a paired Zigbee device (end-device or router). You can't create a zb_device from scratch, they most be retrieved from zigbee object.

zb_device instances can only be read, you can't change directly any attribute.

Instance Variables Parameters and details
shortaddr shortaddr -> int returns the 16 bits short address
longaddr longaddr -> bytes returns the long 64 bits address as 8 bytes (or all zeroes if unknown)
name name -> string returns the friendlyname of the device or 0x.... hex name if no friendlyname was defined using ZbName command
reachable reachable -> bool is the device reachable, i.e. did it respond last time we tried to contact them
hidden hidden -> bool is the device declared as hidden, i.e. not announced in Hue emulation
router router -> bool is the device known to be a router
model model -> string model of the device
manufacturer manufacturer -> string manufacturer name of the device
lastseen lastseen -> int timestamp (epoch) when the device was last seen
lqi lqi -> int radion strength and quality when the device was last seen
battery battery -> int percentage of battery, or -1 if unknwon of no battery
battery_lastseen battery_lastseen -> int timestamp (epoch) when the battery was last reported, or -1

Example:

import zigbee
+
+# show all devices
+for device: zigbee
+  print(device)
+end
+#
+# outputs:
+# <instance: zb_device(0x868E, 0x00124B001F841E41, name:'Bedroom', model:'TH01', manufacturer:'eWeLink')>
+# ... more devices
+
+# read one device by short address
+var device = zigbee[0x868E]
+
+print(device.longaddr)
+# bytes('411E841F004B1200')
+
+print(device.reachable)
+# false - because it's a sleep device
+
+print(device.router)
+# false - it's a sleepy device so not a router
+
+print(device.manufacturer, device.model)
+# eWeLink TH013000_g5xawfcq')>
+
+# example with a plug
+device = zigbee[0xC1BC]
+print(device.longaddr, device.reachable, device.router)
+# bytes('859F4E001044EF54') true false
+print(device.manufacturer, device.model)
+# LUMI lumi.plug.maeu01
+

Changing Zigbee values on-the-fly~

Whenever a Zigbee message is received (typically values of attributes), the Tasmota Zigbee engines generates events at key points which allow custom Berry code to intercept and change messages on-the-fly.

Messages are sent in the following order:

  • frame_received: (low-level) the raw zigbee message is passed as bytes and attributes are not yet decoded. The bytes buffer can be modified and passed back to the Tasmota Zigbee engine.
  • attributes_raw: (mid-level) Zigbee attributes are decoded but no transformation is applied yet. Attributes are only available in cluser/attribute format, names are not decoded and plug-ins are not yet applied.
    This is the perfect moment to change non-standard attributes and map them to standard ones.
  • attributes_refined: (high-level) Attributes are mapped to their names (when possible) and all transformations are applied. This is the last chance to change values.

The format of methods are the following: def <zigbee event>(event_type, frame, attr_list, idx)

Argument Description
event_type (string) can take values: frame_received, attributes_raw or attributes_refined
frame (instance of zcl_frame) low-level ZCL frame
Always present
attr_list (instance of XXX) list of attributes.
This attribute is nil for frame_received, contains raw attributes in attributes_raw and refined attributes in attributes_refined
idx (int 16 bits unsigned) contains the Zigbee short address

Example, if you want to dump all the traffic passed:

import zigbee
+class my_zb_handler
+  def frame_received(event_type, frame, attr_list, idx)
+    print(f"shortaddr=Ox{idx:04X} {event_type=} {frame=}")
+  end
+  def attributes_raw(event_type, frame, attr_list, idx)
+    print(f"shortaddr=Ox{idx:04X} {event_type=} {attr_list=}")
+  end
+  def attributes_refined(event_type, frame, attr_list, idx)
+    print(f"shortaddr=Ox{idx:04X} {event_type=} {attr_list=}")
+  end
+
+end
+
+var my_handler = my_zb_handler()
+zigbee.add_handler(my_handler)
+
+# example of reading for a plug
+#
+# shortaddr=OxC1BC event_type=frame_received frame={'srcendpoint': 21, 'transactseq_set': 0, 'shortaddr': 49596, 'dstendpoint': 1, 'payload': bytes('5500003956CE8243'), 'shortaddr_hex': '0xC1BC', 'manuf': 0, 'payload_ptr': <ptr: 0x3ffccb5c>, 'need_response': 0, 'transactseq': 25, 'cmd': 1, 'direct': 0, 'cluster': 12, 'cluster_specific': 0, 'groupaddr': 0}
+# shortaddr=OxC1BC event_type=attributes_raw attr_list={"000C/0055":261.612,"Endpoint":21,"LinkQuality":21}
+# shortaddr=OxC1BC event_type=attributes_refined attr_list={"ActivePower":261.612,"(ActivePower)":"0B04/050B","Endpoint":21,"LinkQuality":21}
+
+# to remove handler:
+# zigbee.remove_handler(my_handler)
+

The attr_list is of class zcl_attribute_list and can be accessed with zigbee.zcl_attribute_list.

Methods Parameters and details
size size() -> int
Number of attributes in the list
remove remove(index:int) -> nil
Remove the item at index
item
[x]
item(index:int) -> instance or [index:int] -> instance
Retrieve attribute at index, or nil if none.
Note: contrary to native list it does not throw an exception if the index if off bounds.
new_head new_head(attribute:instance of zigbee.zcl_attribute_list) -> self
Adds a new attribute at the beginning (head) of the list
new_tail new_tail(attribute:instance of zigbee.zcl_attribute_list) -> self
Adds a new attribute at the end (tail) of the list

Variables of zcl_attribute_list for the entire list and common to all attributes:

Attributes (read or write) Details
groupaddr uint16 group address if the message was multicast, or nil
src_ep uint8 source endpoint of the message
lqi uint8 lqi for the message received (link quality)

The zcl_attribute_list contains a list of zcl_attribute instance.

Attributes (read or write) Details
cluster uint16 ZCL cluster number
attr_id uint16 ZCL attribute id
cmd uint8 ZCL command number
direction 0 or 1 ZCL direction of the message (to or from the coordinator)
cmd_general 0 or 1 ZCL flag indicating a general command vs a cluster specific command
key string or nil attribute name (if any) or nil
val any ZCL value of the attribute, can be int/float/string/bytes...
key_suffix uint8 key suffix in case a same attribute is repeated
Like Power1, Power2...
manuf uint16 ZCL manufacturer specific code or 0 if none
This is typically indicating a proprietary attribute
attr_multiplier int multiplier to be applied or 1
attr_divider int divider to be applied or 1
attr_base int offset to be applied or 0
attr_type uint8 ZCL type byte for the received attribute

zcl_attribute_list methods:

Methods Parameters and details
tomap tomap() -> map
Transforms main attributes as map (read-only): cluster, attr_id, cmd, direction, key, val

Changing attributes received~

For events attributes_raw and attributes_refined, you receive an instance of attr_list which represents all the attributes received. This list can be modified according to specificities of devices, hence giving full liberty on decoding exotic protocols or manufacturers.

The decoding is done in 2 steps:

  • attributes_raw contains individual attributes with their native raw values. Names are not yet matched, nor scale factors applied. This is where you want to decode non-standard protocols Example: {"000C/0055":261.612,"Endpoint":21,"LinkQuality":21} represents raw value from a plug; the value was decoded as float.

  • attributes_refined contains a similar list with additional decoding handled, any scale factor applied (like transforming integer temperature in 1/100 of Celsius to a float), and human readable names attached. Example: {"ActivePower":261.612,"(ActivePower)":"0B04/050B","Endpoint":21,"LinkQuality":21} In this example, the attribute is 0B04/050B is rename as ActivePower, but the original 0B04/050B attribute cluster/id is still readable. We can see that the generic 000C/0055 (AnalogValue) from lumi.plug.maeu01 is replaced with 0B04/050B (ActivePower).

Changing zigbee frame, zcl_frame class~

The zcl_frame represents a low-level ZCL (Zigbee Cluster Library) structure before any decoding or specific processing. You generally prefer to modify a frame later on when attributes or commands are decoded.

class zcl_frame:

Attributes (read or write) Details
srcendpoint uint8 source endpoint
dtsendpoint uint8 destination endpoint
shortaddr uint16 destination short address
groupadddr uint16 destination multicast group address (if shortaddr is 0xFFFE)
cluster uint16 cluster number
cmd uint8 ZCL command number
cluster_specific flag 0/1 is the command general or cluster specific
manuf uint16 manufacturer specific number (or 0x0000)
needs_response flag 0/1 does this frame needs a response
payload bytes() bytes of the actual data (use with caution, can be read and changed)
  The following are rarely used flags
direct flag 0/1 is the frame to be sent directly only (not routed)
transactseq uint8 transaction number (read only)
transactseq_set uint8 transaction number (write only - if you need to change it)

Example:

frame_received frame_received {'srcendpoint': 21, 'transactseq_set': 0, 'shortaddr': 49596, 'dstendpoint': 1, 'payload': bytes('550039D5787B43'), 'shortaddr_hex': '0xC1BC', 'manuf': 4447, 'payload_ptr': <ptr: 0x3ffd4d04>, 'need_response': 0, 'transactseq': 60, 'cmd': 10, 'direct': 0, 'cluster': 12, 'cluster_specific': 0, 'groupaddr': 0} nil 49596
+

Compiling Berry~

Berry is included if the following is defined in user_config_override.h:

#define USE_BERRY
+

Other options that can be changed:

Option Description
#define USE_BERRY_PSRAM Use PSRAM to allocate memory instead of main RAM. If no PSRAM is connected, this option has no effect.
Enabled by default
#define USE_BERRY_DEBUG Provide additional information in case of a Berry exception, adding line number in the call chain. This feature adds ~8% of memory consumption to Berry compiled code.
Disabled by default
#define USE_WEBCLIENT Enable the webclient module allowing to do HTTP requests.
Enabled by default
#define USE_WEBCLIENT_HTTPS Adds support for HTTPS to webclient. This feature adds ~45KB of Flash space for TLS support.
Disabled by default
#define USE_BERRY_WEBCLIENT_USERAGENT "TasmotaClient" Specifies the default User-Agent field sent by webclient. Can be changed on a per request basis.
#define USE_BERRY_WEBCLIENT_TIMEOUT 5000 Specifies the default timeout in millisecond for webclient. Can be changed on a per request basis.

Berry Cookbook~

Find complete examples and use scenarios of Berry in the Berry Cookbook

\ No newline at end of file diff --git a/Berry_Addressable-LED/index.html b/Berry_Addressable-LED/index.html new file mode 100644 index 0000000000..0e838a2427 --- /dev/null +++ b/Berry_Addressable-LED/index.html @@ -0,0 +1,68 @@ + Addressable LEDs in Berry - Tasmota
Skip to content

Addressable LEDs in Berry~

Requires #define USE_WS2812, included in Tasmota32

Support for addressable leds strips or matrix, including animation. Internally relies on NeoPixelBus library and currently supports WS2812 and SK6812.

How to use~

Compatibility with Templates~

You can control multiple LED strips. WS2812 - 1 is also controlled by Tasmota's light controls. It is still possible to control this light strip with Berry, but whenever you use Tasmota light controls they will temporarily overrid Berry animations.

To avoid any conflict between native WS2812 and Berry control, you can use Scheme 14 which disables native WS2812.

Led strips, matrix and sub-strips~

You first need to define the low-level Leds object that describes the hardware strip of connected leds.

You can then define higher level objects like sub-strips (if there are actually several strips chained together like rings) or LED matrix.

Class Details
Leds Leds(pixels:int, gpio:int [,model:int ,rmt:int]) -> instance<Leds>
Creates a Leds instance for a linear leds strip
pixels: number of leds
gpio: physical gpio number
model: (optional) LED model, default: Leds.WS2812_GRB, alternative Leds.SK6812_GRBW
rmt: (optional) RMTchannel to use, or auto-select (see below)

Once a Leds object, you can use sub-objects:

Method Details
create_matrix <strip>.create_matrix(width:int, height:int [, offset:int]) -> instance<Leds_matrix>
Creates a Leds_matrix instance from a Leds instance
width: number of leds horizontally
height: number of leds vertically
offset: number of leds to skip until start of matrix
You can use set_alternate(true) to enabled alternate lines (i.e. zigzag mode).
create_segment <strip>.create_segment(offset:int, pixels:int) -> instance<Leds_segment>
Creates a virtual segment from a physical Leds strip, from Led number offset with pixels leds.
LED model Details
Leds.WS2812_GRB WS2812b Leds (GRB) - takes 24 bits RGB colors
Leds.SK6812_GRBW SK6812 Leds (GRBW) - takes 32 bits RGBW colors (with white channel)

Methods are the equivalent low-level from NeoPixelBus. All colors are in 0xRRGGBB format (24 bits) or 0xWWRRGGBB format (32 bits).

Attributes Details
clear clear() -> nil
Clear all led (set to black)
clear_to clear_to(col:color [, bri:int]) -> nil
Set all leds to the specified color. bri (0..100) is optional and default to 100%
show show() -> nil
Pushes the internal buffer to leds. May be ignored if a show command is already in progress. Use can_show() to see if show() is possible
can_show can_show() -> bool
Indicates if show() is possible, i.e. no transfer is ongoing
is_dirty is_dirty() -> bool
Indicates if a led was changed since last show()
dirty dirty() -> nil
Forces a refresh during next show()
pixel_size pixel_size() -> int
Returns the number of bytes per pixel
pixel_count pixel_count() -> int
Returns the number of leds in the strip/matrix
clear_to clear_to(col:color [, bri:int]) -> nil
Clears all leds to the specified color. bri is optional and default to 100%
set_pixel_color set_pixel_color(idx:int, col:color [, bri:int]) -> nil
Set led number idx to the specified color. bri (0..100) is optional and default to 100%
set_matrix_pixel_color set_matrix_pixel_color(x:int, y:int, col:color [, bri:int]) -> nil
(only Leds_matrix) Set led number of coordinates x/y to the specified color. bri is optional and default to 100%
set_alternate set_alternate(bool) -> nil
(only Leds_matrix) Sets the matrix as alternate cabling (i.e. zigzag mode) instead of regular mode.
It is common for large led matrix to have every other line in reverse order.
get_alternate get_alternate() -> bool
(only Leds_matrix) Read the value set with set_alternate(bool).
get_pixel_color get_pixel_color(idx:int) -> color:int
Returns the color (including brightness and gamma correction) of led number idx
gamma gamma:bool
Applies gamma correction if true (default)
pixels_buffer pixels_buffer() -> bytes()
Returns the internal buffer used by NeoPixelBus. The byte() object points to the original buffer, no new buffer is allocated; which means that raw data can be changed directly. Don't forget to call dirty() and show() afterwards
set_bytes set_bytes(row:int, buffer:bytes, offset:int, len:int) -> nil (matrix only)
Copy a bytes() buffer directly in the internal matrix buffer, for row row, skipping offset pixels and copying len bytes.

Animation framework~

The class Leds_animator sets the necessary methods to facilitate animations. You just need create a sub-class or Leds_animator, provide a Leds or Leds_matrix instance and implement the animate method. You can also register animators (see below).

The instance is automatically registered as driver. Call start() to start the animation, and stop() to stop it.

Attributes Details
Leds_animator Leds_animator(strip:instance) -> instance<Leds_animator>
Constructors only needs an instance of Leds or Leds_matrix
start start() -> nil
Register the animator as Tasmota driver (with tasmota.add_driver) and start the animation
stop stop() -> nil
Stop the animation and removes the driver from Tasmota drivers list
clear clear() -> nil
Call stop() and clear all leds (set to black)
remove remove() -> nil
Removes the instance from Tasmota's list of drivers, and stops the animation
set_bri set_bri(bri:int) -> nil
Sets the brightness of the animation (0..100)
add_anim add_anim(anim:instance) -> nil
Registers an animator to be called just before the call to animate (see below)
get_bri get_bri() -> int
Returns the brightness of the animation (0..100)
animate animate() -> nil
Place-holder for the actual animation. You need to override this method

Example:

import animate
+class Rainbow_stripes : Leds_animator
+  var cur_offset     # current offset in the palette
+  static palette = [ 0xFF0000, #- red -#
+                     0xFFA500, #- orange -#
+                     0xFFFF00, #- yellow -#
+                     0x008800, #- green -#
+                     0x0000FF, #- blue -#
+                     0x4B0082, #- indigo -#
+                     0xEE82EE, #- violet -#
+                  ]
+
+  # duration in seconds
+  def init(strip, duration)
+    super(self).init(strip)
+    self.cur_offset = 0
+    # add an animator to change `self.cur_offset` to each value of the palette
+    self.add_anim(animate.rotate(def(v) self.cur_offset = v end, 0, size(self.palette), int(duration * 1000)))
+  end
+
+  def animate()
+    var i = 0
+    while i < self.pixel_count    # doing a loop rather than a `for` prevents from allocating a new object
+      var col = self.palette[(self.cur_offset + i) % size(self.palette)]
+      self.strip.set_pixel_color(i, col, self.bri)   # simulate the method call without GETMET
+      i += 1
+    end
+    self.strip.show()
+  end
+end
+

How to use:

var strip = Leds(5,5, gpio.pin(gpio.WS2812, 1))
+var r = Rainbow_stripes(strip, 1.0)
+r.start()
+

And here is another example that "breathes" the LED strip with a hardcoded colour:

class Breathe : Leds_animator
+  var brightness
+  var colour
+
+  # duration in seconds
+  def init(strip, duration)
+    super(self).init(strip)
+    self.brightness = 0
+    self.colour = 0xFFFFFF
+    self.add_anim(animate.back_forth(def(v) self.brightness = v end, 0, 100, int(duration * 1000)))
+  end
+
+  def animate()
+    var i = 0
+    while i < self.pixel_count    # doing a loop rather than a `for` prevents from allocating a new object
+      self.strip.set_pixel_color(i, self.colour, self.brightness)
+      i += 1
+    end
+    self.strip.show()
+  end
+end
+

And to use this one:

var strip = Leds(5,5, gpio.pin(gpio.WS2812, 1))
+var r = Breathe(strip, 2.0)
+r.start()
+

Advanced features~

Hardware RMT channels~

This library uses NeoPixelBus library, and RMT hardware support in ESP32. The number of RMT channels, hence the number of simultaneous strips, depends on the CPU type. Tasmota native support for WS2812 uses RMT channel 0; it is not usable in such case.

CPU type RMT channels
ESP32 8
ESP32S2 4
ESP32C3 2

Currently RMT channel 0 is used by default if no GPIO WS2812-1 is configured, RMT channel 1 otherwise.

\ No newline at end of file diff --git a/Blinds-and-Shutters/index.html b/Blinds-and-Shutters/index.html new file mode 100644 index 0000000000..3600c43ad0 --- /dev/null +++ b/Blinds-and-Shutters/index.html @@ -0,0 +1,52 @@ + Shutters and Blinds - Tasmota
Skip to content

Shutters and Blinds~

Control blinds and roller shades connected to regular ON/OFF motors,stepper motors or position servos

Before starting you have to enable shutter support with SetOption80 1

There is a new enhanced ESP32 version you can enable with the following compiler option #define USE_SHUTTER_ESP32. Will be default in future versions for ESP32 after some testing. New features most likely only in ESP32 due to memory limitations in ESP8266. Current feature set:

  • up to 16 shutters,
  • up to 32 shutterbuttons,
  • tilt definition with buttons.
  • Configuration saved to filesystem.
  • shuttersetup for Shelly plus 2PM to automatically measure open and close duration

Commands~

Complete list of commands is available at Blinds, Shutters and Roller Shades Commands.

Shutter Modes~

There are five shutter modes which define how the relays operate. Additionally you can define PulseTime on any relay to change the relay into a pulse relay where the pulse changes start/stop. At least for Shutter mode 1 an interlock is mandatory Interlock.

The examples below are for a ShutterRelay1 1 configuration (using Relay1 and Relay2).

Shutter mode 1 - Normal Operation

Relay1: UP/OFF, Relay2: DOWN/OFF

  • Interlock 1,2 (Interlocked relay pair)
  • Interlock ON

Shutter mode 2 - Circuit Safe (must be set manually)

Relay1: ON/OFF, Relay2: UP/DOWN

  • Interlock OFF

Shutter mode 3 - Garage Motors (must be set manually)

Relay1: OFF/DOWN PULSE, Relay2: OFF/UP PULSE

Shutter mode 4 - Stepper Motors (autodetect with PWM and COUNTER)

Relay1: ON/OFF, Relay2: UP/DOWN

  • PWM: Stepper signal, COUNTER: Stepper position signal
  • PWM and COUNTER defined

Shutter mode 5 - Servo Motors (PWM position based servo)

Relay1: ON/OFF, Relay2: UP/DOWN (optional not used)

  • PWM: Stepper signal
  • PWMfrequency 200 ( This is mandatory for most relay to get correct PWM duty cylces)
  • SetOption15 0 (required to store value and make it reboot save)

Shutter mode 6 - Servo Motors 360° (PWM speed based servo)

Relay1: ON/OFF, Relay2: UP/DOWN (optional not used)

  • PWM: Stepper signal
  • PWMfrequency 200 ( This is mandatory for most relay to get correct PWM duty cylces)
  • SetOption15 0 (required to store value and make it reboot save)

Wiring diagrams for Normal, Stepper motor, and Short Circuit-Safe configurations are available at the end of this page. Even if the shutter does not have two motors, three wires have to be connected.

Note

After setting the options for shutter mode, the device should be rebooted. Otherwise, the sliders won't be available in the web UI, and the ShutterOpenDuration<x>and ShutterCloseDuration<x> commands will report "Shutter unknown".

Issue Shuttermode command and check in console which ShutterMode is displayed: Issue Status 13 command and check in console how the shutter is defined

If you define Shuttermode 1 and there is NO interlock defined on the relay the driver go into ERROR state. To solve define INTERLOCK on the relay pair and set it to ON. Then define ShutterRelay1 again.

Shutter accuracy digits: 1
+Shutter 0 (Relay:1): Init. Pos: 20000 [100 %], Open Vel.: 100 Close Vel.: 100 , Max Way: 20000, Opentime 10.0 [s], Closetime 10.0 [s], CoedffCalc: c0: 0, c1 200, c2: 200, c3: 0, c4: 0, binmask 3, is inverted 1, <span style="font-weight:bold;color:lime">ShutterMode 0</span>, motordelay 0
+

Operation~

Turning a device relay on or off directly (i.e., using Power) will function to affect a shutter's movement. In momentary mode (i.e., stepper motor), the relays start or stop the motor. The driver takes care of the direction and proper update of the shutter position.

The shutter reports its position and can also be sent to a dedicated position. ShutterPosition 0 means the shutter is closed and ShutterPosition 100 means the shutter is open. If you need the position values reversed (0 = open, 100 = closed), define and calibrate your shutter as documented below. Then tell Tasmota to reverse the shutter position meaning via the ShutterInvert<x> 1 command. All internal calculations are the same (the log output is the same). Only the interaction with the user and other systems changes. Now ShutterPosition<x> 0 will open the shutter and ShutterPosition<x> 100 will close the shutter.

Function ShutterInvert 0 ShutterInvert 1
Relay 1 open shutter open shutter
Relay 2 close shutter close shutter
Webbutton UP open shutter open shutter
Webbutton DOWN close shutter close shutter
Slider OPEN open shutter open shutter
Slider CLOSE close shutter close shutter
ShutterOpen open shutter open shutter
ShutterClose close shutter close shutter
ShutterPosition 100 open shutter close shutter
ShutterPosition 0 close shutter open shutter
Reported position when open 100 0
Reported position when close 0 100

To set a value to all shutter you can use the index 0. For example ShutterPosition0 30 will move all shutters to 30%. The index 0 also works with all configuration items.

When SetOption80 1 is invoked you have to define Shutterrelay1 <value> to get started If possible to avoid any injury on unexpected movement all RELAYS should start in OFF mode when the device reboots: PowerOnState 0

A maximum of four shutters per device are supported on ESP8266. The newer ESP32 can support up to 16 Shutters with 32 Relays. Nevertheless it might be required to add a GPIO expension board to get enough connectors.

To enable additional shutters, ShutterRelay<x> <value> must be executed for each additional shutter. Additional shutter declarations must be sequentially numbered, and without gaps (i.e., second shutter is 2, next shutter 3 and finally shutter 4).

Disabling a shutter in the middle of the defined set of shutters will disable all other higher numbered shutters. If the disabled shutter is restored, the higher numbered shutters previously declared will also be restored. When a shutter is added or removed, a list of the active shutters, with their parameters, is output to the log. If you intend to remove shutters, explicitly remove each one beginning with the highest numbered shutter.

With four shutters, eight Relay<x> components are needed. If manual operation switches (Switch<x> or Button<x> pairs) are also used, additional input GPIO are required. The ESP82xx device may not have enough free GPIO to support all the shutter connections required. A GPIO expander such as a PCF8574 or MCP230xx can be used with additional effort.

When using a switch for manual operation Switch<x> pairs should usually be set to SwitchMode<x> 2 (inverse follow) for proper switch behavior.

Any shutter positioning can be locked ShutterLock<x> 1. Once executed an ongoing movement is finished while further positioning commands like ShutterOpen<x>, ShutterClose<x>, ShutterStop<x>, ShutterPosition<x>, ... as well as web UI buttons, web UI sliders, and shutter buttons are disabled. This can be used to lock an outdoor blind in case of high wind or rain. You may also disable shutter positioning games by your children. Shutter positioning can be unlocked using ShutterLock<x> 0. Please be aware that the shutter can still be moved by direct relay control (i.e., Power<x>), or physical switches and buttons. Use the ShutterButton<x> command prior to ShutterLock to be able to lock buttons.

AutoSetup (Only Shelly plus 2PM, ESP32 based)~

The shelly plus has enough memory and a power measuring unit to setup the shutter in convinent way. First you must callibrate your mechanical endstops of the shutter. Please do as descibed in the documentation of your shutter motors to ensure the shutter will stop at the endpoint correctly.

Then close the shutter until endstop is reached (repeat: backlog shuttersetopen;shutterclose until closed) - interlock 1,2 - interlock on - shutterrelay1 1 - shuttersetup (shutter will start moving....)

After setup is started the shutter will move to the upper endpoint and close again. If the shutter stops somewhere in the middle, try again. After setup you can use your shutter. Initial callibration for ShutterSetHalfway is 70. If you want to have it more accurate: backlog shutterclose;ShutterSetHalfway 50 and follow instructions below.

Calibration~

Shutter calibration video tutorial

Shutter calibration Google Spreadsheet

  • Start your shutter in a closed position preferably. Set internal position to closed with ShutterSetClose<x>.
  • Set the time needed to open the shutter completely with ShutterOpenDuration<x>.
  • If the shutter opens more than needed, move it to the desired position with ShutterSetPosition<x> then set the position to fully open (100) with ShutterSetOpen<x> and decrease the open time.
  • Set the time needed to close the shutters with ShutterCloseDuration<x>.
  • If the shutter does not close completely, open again and adjust close time.
  • If it runs too long, move it back to desired closed position with ShutterSetPosition<x>, reset to 0 with ShutterSetClose<x> and decrease open time.
  • Alternate between opening and closing the shutter until you find out the exact times needed to get the same positions multiple times
  • Now set the 50% open position of the shutter. Some shutters need some time from totally closed until they begin moving the bottom-most part and opening. This often results in a shutter that is less than 50% open when the shutter has been operating for 50% of the set time. This can be corrected by using ShutterSetHalfway<x>. Use this procedure to calibrate the half-open position:
  • ShutterClose<x> (confirm that the shutter is completely closed)
  • ShutterSetHalfway<x> 50 (reset to default)
  • Move the shutter to actual 50% open position.
  • Use ShutterPosition<x> to inquire the shutter's current position and record the value. This value is a percentage of the total opening (e.g., 63 = 63% of opening).
  • ShutterClose<x>
  • ShutterSetHalfway<x> 63 (using the value from step #4 above)
  • Restart 1

After calibration is complete, you might want to enable an additional 1 second motor movement with ShutterEnableEndStopTime<x> 1 when the shutter is asked to move to its end positions (0% and 100%). With this you can guarantee that end positions are still reached in case of inaccuracies. Take care to disable this with ShutterEnableEndStopTime<x> 0 before further open or close duration measurements.

Increasing Calibration Granularity~

If you desire that the %-opening closely match what ShutterPosition<x> and web UI indicate, there is a granular calibration matrix available. Ensure that ShutterClose<x> and ShutterOpen<x> moves the shutter more or less to the limit positions and follow this procedure:

  • ShutterSetHalfway<x> 50 (reset to default)
  • ShutterCalibration<x> 30 50 70 90 100
  • Restart 1
  • ShutterClose<x>
  • Move the shutter to each of the following opening percentages and measure the shutter's position for each.
  • ShutterPosition<x> 30 (e.g., measurement = 15)
  • ShutterPosition<x> 50 (e.g., measurement = 50)
  • ShutterPosition<x> 70 (e.g., measurement = 100)
  • ShutterPosition<x> 90 (e.g., measurement = 150)
  • ShutterPosition<x> 100 (e.g., measurement = 180)
  • Finally, enter the position measurements as the calibration values: ShutterCalibration<x> 15 50 100 150 180

ShutterCalibration<x> takes position measurements (not the time it takes to move). During calibration you position the shutter to an indicated percentage (e.g., 30%) of opening and measure the shutter position (e.g., 15). Use the same unit of measure for all your measurements (e.g., centimeters, inches, steps, etc.). After calibration ShutterPosition<x> 30 will move to 30% opening. This will be 30% from 180 (full open) == 54. Now the percentage match the percent in cm/inch/steps.

Notice that there is no calibration for the 10% position. On many shutters, there is no movement during the initial phase (i.e., nearly 10% of total time). Therefore the opening could be 0. This measurement would cause an execution DIV 0 exception. Therefore the first calibration point is 30%. In most cases this is not a large opening so the calibration will be near enough. Yes, until ~10%, the position will be a bit "off" but not enough for concern.

Motor Movement Delays~

Some motors need up to one second after power is turned on before they start moving. You can confirm if you are having this issue if opening and closing as a single action works properly but doing this in smaller steps result in a shift of the position.

  1. Shutterposition<x> 30
    Measure the shutter position. This is the reference_position
  2. Shutterposition<x> 80
    Measure the shutter position. This is the max_position
  3. Shutterposition<x> 30
    Return the shutter to starting position. This must be the same position as measured in step #1 (reference_position). If not, ShutterCloseDuration must be adjusted.
  4. Shutterposition<x> 50
  5. Shutterposition<x> 70
  6. Shutterposition<x> 80
    If you do not reach max_position you have a motor delay problem. Measure the shutter position. This is the real_max. Use this value in the calculation below.
  7. ShutterMotorDelay<x> <delay>
    Motor <delay> calculation - fine tune in 0.05 second increments (e.g. 0.65) as required.
    <delay> = ((max_position-real_max) / 2) / (((100/80) * max_position) / ShutterOpenDuration)

Close the shutter and repeat this procedure until the motor delay is set properly.

Following defaults are pre-compiled into the code and can only be changed by compiling you own binary and use the user_config.override - In Failsafe-Mode the driver waits for 0.1sec to let the direction relay execute and be stable before switching on the power relay starting the movement. The time in [ms] can be changed by adding following line with a different value: #define SHUTTER_RELAY_OPERATION_TIME 100 // wait for direction relay 0.1sec before power up main relay -

Motor Stop time~

When shutters change direction immediatly it can happen that there is a short circuit or at least high moments on the motors. Therfore the default time between a STOP and the next START of the shutter is 0.5s = 500ms. This allows in most cases the shutter to fully stop and then start from a static position. With Version 12.3 you can change the duration through shuttermotorstop 500 or any other value in ms. The value is for all defined shutters the same.

WARNING: If you control the relay DIRECT through buttons or switches and do not use shutterbuttons or a rule to decouple it, then the MOTOSTOPTIME cannot kick in. The Relay is ON before the shutterdriver can intercept.

Button Control~

When shutter is running in ShutterMode 1 (normal two relay up/off down/off), you already have basic control over the shutter movement using switches or buttons in the module configuration to directly drive the shutter relays. For short circuit safe operation ShutterMode 2 direct control of the relays will not give you a nice user interface since you have to 1st set the direction with one switch/button and 2nd switch on the power by the other switch/button. Because the button controll use multi-press events ensure that the "immediate action" is disabled: SetOption13 0 (default)

To have shutter mode independent button control over the shutter and not over its relays one can use the ShutterButton<x> command. It also introduces some more features, see below:

ShutterButton<x> <button> <func> <mqtt>

This assigns a Tasmota button <button> to control your shutter <x> having functionality <func>. The Tasmota button <button> must already be configured in the module configuration. You can assign multiple buttons to a single shutter. Any button can only control one shutter (beside the <mqtt> broadcast feature, see description below). Any press of the button while the shutter is moving will immediately stop the shutter.

One can remove all button control for shutter <x> by ShutterButton<x> 0.

The assigned button can have one of the following functionalities:

  • Setup for an "up" button: ShutterButton<x> <button> up <mqtt>
    Single press will move shutter to 100%, double press to 50% and triple press to 74%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> to 100% when <mqtt> is equal to 1. When <mqtt> is equal to 0 hold action of this button is same as single press.

  • Setup for a "down" button: ShutterButton<x> <button> down <mqtt>
    Single press will move shutter to 0%, double press to 50% and triple press to 24%. Holding the button for more than the hold time (SetOption32) moves all shutters with same <grouptopic> to 0% when <mqtt> is equal to 1. When <mqtt> is equal to 0 hold action of this button is same as single press.

  • Setup for an "updown" button: ShutterButton<x> <button> updown <mqtt>
    Single press will move shutter to 100%, double press down to 0% and triple press to 50%. No hold action and no other shutter control by MQTT, <mqtt> is don't care here.

  • Setup for a "toggle" button: ShutterButton<x> <button> toggle <mqtt> Single press will toggle shutter, double press will move it to 50%. Be aware that the toggle select direction based on the current position. If the position is between 0..50% the shutter move to 100%. If the position is 51%..100% it moves to 0%. No hold action and no other shutter control by MQTT, <mqtt> is don't care here.

More advanced control of the button press actions is given by the following ShutterButton<x> command syntax:

ShutterButton<x> <button> <p1> <p2> <p3> <ph> <m1> <m2> <m3> <mh> <mi>

<button> ESP8266:1..4,ESP32:1..32 : Button number, 0/-: disable buttons for this shutter
<p1> 0..100: single press position, t: toggle, -: disable, --0..100 or ++0..100 single press increment position
<p2> 0..100: double press position, t: toggle, -: disable, --0..100 or ++0..100 double press increment position
<p3> 0..100: triple press position, t: toggle, -: disable, --0..100 or ++0..100 tripple press increment position
<ph> 0..100: hold press position, shutter stop after releasing the hold button if no group send <mh> is defined. If<mh> is 1 the release of the botton will NOT stop any shutter, t: toggle, -: disable
<m1> 1: enable single press position MQTT broadcast, 0/-: disable
<m2> 1: enable double press position MQTT broadcast, 0/-: disable
<m3> 1: enable triple press position MQTT broadcast, 0/-: disable
<mh> 1: enable hold press position MQTT broadcast, 0/-: disable
<mi> 1: enable MQTT broadcast to all shutter indices, 0/-: disable

Parameters are optional. When missing, all subsequent parameters are set to disable.

ESP32 only: <p0..h> can be optional extended with a dedicated position of the tilt if a venetian blind is configured and supported. The position of the tilt can be added after the normal position with a / as seperator. This is optional. Example: shutterbutton1 1 100/-90 50/0 75/-90 100 - - 1 1. Tilt and position also support increment or decrement position from current state with --oder ++.

By a button single press the shutter is set to position <p1>. Double press will drive the shutter to position <p2> and triple press to position <p3>. Holding the button for more than the SetOption32 time sets the shutter position to <ph> max if button is hold until position. If the hold button is released during the shutter moves the shutter will stop. Any button action <p1> to <ph> can be disabled by setting the parameter to -. Independent from configuration <p1> to <ph> any press of the button while the shutter is moving will immediately stop the shutter.

Global steering of all your shutters at home is supported by additional MQTT broadcast. By any button action a corresponding MQTT command can be initiated to the <grouptopic> of the device. For single press this can be enabled by <m1> equal to 1, disabling is indicated by -. Double to hold MQTT configurations are given by <m2> to <mh>, correspondingly. When <mi> is equal to - only cmnd/<grouptopic>/Shutterposition<x> <p1..h> is fired. When <mi> is equal to 1, <x>=1..4 is used to control any shutter number of a Tasmota device having same <grouptopic>.

Example

  • ShutterButton<x> <button> 100 50 74 100 0 0 0 1 1 is same as ShutterButton<x> <button> up 1.
  • ShutterButton<x> <button> 0 50 24 0 0 0 0 1 1 is same as ShutterButton<x> <button> down 1.
  • ShutterButton<x> <button> 100 0 50 - 0 0 0 0 0 is same as ShutterButton<x> <button> updown 0.
  • ShutterButton<x> <button> t 50 - - 0 0 0 0 0 is same as ShutterButton<x> <button> toggle 0.

Module WiFi setup, restart, upgrade and reset according to Buttons and Switches are supported "child and fool proof" only when no button restriction (SetOption1) is given and when all configured shutter buttons of that shutter are pressed 5x, 6x, 7x times or hold long simultaneously.

Remote Control~

Use any other Tasmota device with buttons or switches to control remotely a shutter using rules. Similar behavior as direct button control can be achieved by applying ShutterStopClose, ShutterStopOpen, ShutterStopToggle, ShutterStopPosition commands. They stop shutter movement if it is in motion and otherwise execute close, open, toggle or position commands.

Example

Run this rule on another Tasmota device with a switch configured. rule1 on switch1#state=2 do publish cmnd/%shutter-topic%/ShutterStopToggle endon

Specific Configuration~

Note

The PWM remains on even after the end position has been reached. The motor then permanently tries to hold the position and could thereby trigger noises or a slight "twitching". If this is not desired, you can switch off the PWM after reaching the end position with #define SHUTTER_CLEAR_PWM_ONSTOP.

Pulse Motors~

There are shutters that have two relays but only need a pulse to start or stop. Depending on the current situation a pulse will stop the shutter or send it into a specific direction. To use these kinds of shutters a PulseTime must be defined on each relay. The minimum setting that seems to make it work consistently is 2. A setting of 1 does not work. If the shutter moves too fast and does not react to a stop command, increase the setting to 3 or 4.

Stepper Motors~

Stepper motors can also be used to operate shutters and blinds. Additionally you can operate sliding doors with this configuration. Currently ESP32 12.0.2 does not support shutter with stepper motors. ESP8266 and ESP32 12.0.3+ supports up to 4 shutters.

Servo Motors~

Servos are small devices with typical 180° or 360" rotation movement. The position will be drived by the PWM duty cycle time. This will all automatically calculated

If you miss low angles (i.e 2°) you should be able to get this by changing the minimum ShutterPWMRange. If this does not help you can customize PwmFrequency. Normally for PWM servos, there is no calibration required, but you have the option to do a calibration. Check the documentation for shuttercallibration. Maybe ShutterSetHalfway is already enough. Otherwise you can do a fine granular calibration.

If you change the shutteropenduration/closeduration the servo will operate slower, but now the servo also achieves small angle changes.

Servo controlled by PWM pulses. The angle to which the servo should be set is regulated by the pulse width. The servo has no direction of rotation (clockwise or counterclockwise), but only the rotation angle of the output shaft.

Servo controlled by PWM pulses. The angle to which the servo should be set is regulated by the pulse width. The servo has no direction of rotation (clockwise or counterclockwise), but only the rotation angle of the output shaft.

To operate a servo requires signal uses:

  • EN (enable) turn on / off servo.
  • PLS (pulse) for controls rotation angl.
  • DIR (direction) only shows the direction of rotation, but does not control it.

More information:~

Example configuration~

EN and DIR are on Relay1 and Relay2 respectively. Remember to use a non-inverse relay for the enable signal. The PLS signal is assigned as a PWM<x> component where <x> matches the number of the shutter (e.g., PWM1 for Shutter1).

The PWM frequency must be set with the PWMfrequency 200 command. The frequency setting is a global setting all PWM components on the device. This means that all shutters on the device will operate at the same speed.

Using the ShutterMotorDelay command to provide a slow increase / decrease in speed. This causes the driver to ramp the speed up and down during the defined duration. The change of the ShutterMotorDelay does NOT change the distance the shutter makes. This is very convinent to trim the accelerate and decelerate rate without changeing the distance.

ShutterOpenDuration and ShutterCloseDuration define the open / close time. By changing the times you adjust the speed of the servo output shaft. ShutterOpenDuration and ShutterCloseDuration can be different.

Using the ShutterPwmRange command to set the pulse width range. The calculation of parameters for ShutterPwmRange is obtained by dividing by 5 the minimum and maximum values of the pulse width range.

Note!~

Even within the same servo model, there may be manufacturing tolerances that result in a different operating range of pulse lengths. For accurate operation, each specific servo must be calibrated: through experiments, it is necessary to select the correct range that is specific to it.

For example the pulse width range for different servos:

  • Servo MG90 (180°): pulse width range 544 - 2400microseconds, ShutterPwmRange 109, 480
  • Servo TD-8130MG (180°): pulse width range 500 - 2500microseconds, ShutterPwmRange 100, 500

Servo datasheet:~

How to use it:~

Wemos Pin GPIO Component Servo Signal
D3 0 Relay1 EN
D4 2 PWM1 PLS
D5 14 Relay DIR
  • You must add support for Shutter in my_user_config.h file (оr flash your ESP8266 module with tasmota.bin file).
  • Run commands in the console to run the "Shutter" mode (you must first configure the GPIO!):
    SetOption80 1 // enable Shutters support.
    shutterrelay1 1 // Define that RELAY1 is for ON/OFF and PWM1 drive position
    Shuttermode 5 // enable Shutter mode for servo.
    PWMfrequency 200 // this is a global variable for all Servos.
    SetOption15 0 // to control the storage of values.
  • Run commands in the console to configure the motor operation:
    ShutterPwmRange1 100, 500 //this is a global variable for all Servos.
    ShutterOpenDuration1 0.5 // define the open time, in seconds.
    ShutterCloseDuration1 0.5 // define the close time, in seconds.
    ShutterMotorDelay1 0.2 // servo does not like abrupt start / stop.
    Restart 1
  • Run commands in the consolee to test the motor operation:
    ShutterOpen1 // to open the SERVO#1.
    ShutterStop1 // to stop the SERVO#1.
    ShutterClose1 // to close the SERVO#1.
  • Perform the shutter calibration (optional).

Motor Wiring Diagrams~

One Shutter~

511

  • Diagram v512: Minimum configuration for one servo, power on continuously. 512
Dual Shutter~

521

  • Diagram v522: Optimal configuration for two servos, power is supplied through the relay. 522
  • Diagram v532: Maximum configuration for two servos, power is supplied via the relay only if the servo is running. Current control for each servo drive via INA219. 532

Additional Information:~

DC Motors~

More info

Smooth RAMP-UP and RAMP-DOWN Support~

Servos and Steppers also have a velocity control. With ShutterMotorDelay<x> 1.5 you can define a 1.5second soft start/stop before the device reaches it final moving speed. Usefull for moving heavy items like doors.

using normal Motors~

Short Circuit safe wire configuration with a PCF as digital I/O. Avoid electrical shortage also on wrong configuration.~

ShortCicuitSafe

using Stepper Motors~

Stepper motors can be used to operate shutters and blinds. The configuration is very similar to the Circuit Safe (Shuttermode 1) configuration. To operate a stepper motor requires driver module such as the A4988 and uses EN (enable), DIR (direction), STP (Stepper) for controls. If everything is defined correctly Shuttermode 4 will be reported at boot time. ESP32 only supports one shutter with steppermotors. ESP8266 up to 4.

Tasmota supports a maximum of four shutters with one stepper motor per shutter simultaneously. In very rare conditions where two or more shutters simultaneously move the last mm it can happen than one shutter moves to far.

Example configuration~

EN and DIR are on Relay1i and Relay2 respectively. Please be aware to use the inverse relay for the enable signal.

The STP signal is assigned as a PWM<x> component where <x> matches the number of the shutter (e.g., PWM1 for Shutter1). The shutter feature adjusts the PWM frequency to operate the motor for proper shutter operation. The stepper motor frequency setting is a global setting all PWM components on the device. This means that all shutters on the device will operate at the same speed. Therefore no PWM devices other than shutters can be connected to the same Tasmota device.

The frequency of the PWM can be changed from 1000Hz to any value up to 10,000Hz. The command ShutterFrequency globally changes this. Be aware that most 12V operated motors cannot work faster than 2,000Hz. 5,000Hz.10,000Hz is possible by increasing the supplied voltage to 24V and use ShutterMotorDelay to allow a slow speed up/speed down. The maximum voltage of the A4988 is 36V. The TMC2208 is much more silent than the others but also significant slower and does not like high frequencies. For example, the speed at 24V is half o A4988

Finally a GPIO must be assigned as Counter1. This counter is used to keep track of the steps and send the stepper to the correct position. The Counter1 GPIO must be connected to the PWM1 GPIO. Otherwise the stepper and your shutter will run continually or freeze up randomly.

Only bipolar stepper motors may be used (see above).

You must properly configure the stepper motor driver (see above).

ShutterOpenDuration and ShutterCloseDuration can be different. Shutter with Stepper motors always match positions exact. There is no need to vary ShutterOpenDuration and ShutterCloseDuration. Anyhow, if you decrease ShutterCloseDuration the Shutter will close with a higher speed on a virtual higher ShutterFrequency if possible. Same vice versa.

You can define a soft start/stop by defining a ShutterMotorDelay. This causes the driver to ramp the speed up and down during the defined duration. The change of the ShutterMotorDelay does NOT change the distance the shutter makes. This is very convenient to trim the accelerate and decelerate rate without changing the distance.

Wemos Pin GPIO Component Stepper Signal
D1 5 Relay1i EN
D2 4 Relay2 DIR
D3 0 PWM1 STP
D4 2 Counter1 STP

a) Set ShutterMode 4
Backlog PulseTime1 0; PulseTime2 0 // for relay Relay1i and Relay2
Interlock OFF // this is a global variable for all Relays or at least the RELAYS NOT in the Interlock group PWM1 and COUNTER1 defined

b) Enable Shutters
SetOption80 1 // this is a global variable for all Shutters

c) Configure Shutter 1 and test ShutterMode 1 is working
ShutterRelay1 1 // for relay Relay1i and Relay2

d) Set the stepper motor speed (optional setting)
ShutterFrequency 1500 // this is a global variable for all steppers (1000rpm by default)

e) Set at least a small ramp-up/ramp down period 1.0 second (optional)
ShutterMotorDelay1 1.0 // Stepper do not like infinite momentum. Ramp up/down speed allow much higher frequencies.

f) Restart Tasmota
Restart 1

g) Test the shutter
ShutterOpen1
ShutterStop1 // to stop the STEPPER1
ShutterClose1
ShutterInvert1 // to change the direction of rotation of the STEPPER1

h) Perform the shutter calibration

Configuration for additional shutters~

You must first set up the first shutter and only then the next.

Wemos Pin GPIO Component Stepper Signal
D5 14 Relay3i EN
D6 12 Relay4 DIR
D7 13 PWM2 STP
D8 15 Counter2 STP

a) Set ShutterMode 4
Backlog PulseTime3 0; PulseTime4 0 // for relay Relay3i and Relay4
PWM2 and COUNTER2 defined

c) Configure Shutter 2 and test ShutterMode 1 is working
ShutterRelay2 3 // for relay Relay3i and Relay4

b) Restart Tasmota
Restart 1

d) Test the shutter
ShutterOpen2
ShutterStop2 // to stop the STEPPER2
ShutterClose2
ShutterInvert2 // to change the direction of rotation of the STEPPER2

e) Perform the shutter calibration

Motor Wiring Diagrams~

One Shutter~

411 - Diagram v412: simple universal setup. For example, the control of horizontal curtain or vertical shutters, blinds adjuster or window opener, pet feeders, opening of a water tap for watering the lawn, rotating table for subject photography, opening the ventilation flap, PTZ camera, 3D Scanner Table, linear Actuator.
412 - Diagram v414: parallel setup is to run two parallel steppers motors from the same controller. For example, to control a large and heavy hanging screen for an LCD projector, or two curtains at once on one large window.
414 - Diagram v416: minimum setup size. For example, for small curtains located in a limited space.
416

2 Shutters~

421 - Diagram v422: parallel setup is to run two shutters and independent control of two stepper motors from one controller. For example, to control two independent curtains.
422 - Diagram v424: big parallel setup is to run two shutters and independent control of two pairs of stepper motors from one controller. For example, to control four curtains on one very large window.
424

Bill of Materials~

using Sonoff Dual R2~

If using a Sonoff Dual R2, use the following Template:

{"NAME":"Sonoff Dual R2","GPIO":[17,255,0,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":39}
+

Checklist~

  • Ensure that the first relay opens the shutter
  • Ensure that the second relay closes the shutter
  • Set ShutterRelay<x>
  • Set ShutterOpenDuration<x>
  • Set ShutterCloseDuration<x>
  • Set ShutterSetHalfway<x> (optional)
  • Set ShutterInvert<x>(optional)
  • Set ShutterInvertWebButtons<x>(optional) (eg. useful for horizontal awnings)
  • If the shutter uses a pulse motor instead of a motors with one wire for each direction (i.e., duration based), define PulseTime<x> 2 on both relays. The driver's behavior will change to a pulse motor that needs pulses to start and stop.

Rules~

Tasmota rule triggers:

  • Shutter<x>#Position is triggered at start, during and at the end of movement reporting actual position (%value%=0-100)
  • Shutter<x>#Direction is triggered at start, during and at the end of movement reporting actual direction (%value%: -1=close direction, 0=no movement, 1=open direction)
  • Shutter<x>#Target is triggered at start, during and at the end of movement reporting current target (%value%0-100)
  • Shutter#Moving is triggered during movement and just before moving (shutter independently). If VAR<x> is set to 99 then this trigger will be executed BEFORE the shutter starts. You can start the shutter after rule execution by setting VAR<x> to 0 or wait 10seconds before the timeout kicks in. After the movement with shutter#moved the VAR must be set back to 99 and the initial rule must be enabled again. The initial rule has to be defined to run ONCE. EXAMPLE: power3 on and wait 2sec before start. After movement: power off
    rule1 on shutter#moving=1 do backlog power3 on;delay 20;var1 0 endon
    +rule1 5
    +rule2 on shutter#moved do backlog power3 off;var1 99;rule1 5 endon
    +rule2 1
    +
  • Shutter#Moved is triggered at end of movement (shutter independently)
  • Shutter<x>#Button<button>=0 is triggered when button is hold
  • Shutter<x>#Button<button>=<n> is triggered when button is pressed n times
  • Shutter<x>#Button0=0 is triggered when all buttons of that shutter are hold simultaneously
  • Shutter<x>#Button0=<n> is triggered when all buttons of that shutter are pressed simultaneously n times

Examples

  • Publish a message with the position of the shutter: Rule1 ON Shutter1#Position DO Publish status/%topic%/level {"%value%"} ENDON
  • Open/Close or set a specific position for a shutter. This example drives the second shutter to the same position as the first shutter:
    Rule1 ON Shutter1#Position DO ShutterPosition2 %value%" ENDON

Jarolift Shutter Support~

Jarolift shutters operates by the 3 commands up/stop/down. Compile with the KeeLoq Option and provide the extracted master keys to communicate. Please see KeeLoq description how to do that. After this create a rule to allow the shutter to control the Jarolift devices. Shutter must be in ShutterMode 0.

Rule1 On Power1#state=0 DO KeeloqSendButton 4 endon On Power2#state=0 DO KeeloqSendButton 4 endon on Power1#state=1 DO KeeloqSendButton 8 endon on Power2#State=1 DO KeeloqSendButton 2 endon

Venetian Blind Support~

All time based shutters (not stepper, pwm) can be enhanced with Venetian Blind functionality. The configuration need following parameters: angle of blinds during opening phase, angle of blinds during closing phase. This are the max and the min values of the venetian blinds (e.g. opening at 0° - closing at 90°). Additionally the runtime is required from min to max. This is typically 1-2sec. The resolution of the time is 0.05sec. Duration in [sec] must be multiplied by 20. e.g. 1.2sec => 1.2 x 20 = 24. To open and close the tilt you must define the angle for CLOSE and the angle for OPEN of the tilt (be careful about the correct order, refer to the following example)

Example: - Angle of the slats when the blind is opening: 0° - Angle of the slats when the blind is closing: 90° - Time needed to flip in 1/20 seconds (1 second = 20): 40 - Desired angle of the slats when set to CLOSE : 90° - Desired angle of the slats when set to OPEN : 0°

ShutterTiltConfig1 0 90 40 90 0

Tilt configuration can be set for every shutter independently. The tilt can be set with one of the following commands: shuttertilt1 open set tilt to defined open angle shuttertilt1 close set tilt to defined close angle shuttertilt1 20 set tilt to 20° angle

If the shutter is moved from one position to another position the tilt will be restored AFTER the movement. If the shutter is fully opened or fully closed the tilt will be reset. This means there is no tilt restore at the endpoints. If you want to restore the tilt at the endpoint you have two options to do that: - use a backlog command e.g. backlog shutterclose1; shuttertilt1 open - use shutterposition1 0,90 with an optional position of the tilt as a second parameter

Similar to shutterchange to make relative movements there is also a shuttertiltchange with the same behavior.

If the shutter is operated with wall buttons or the web interface and stopped during a tilte change before the shutter starts moving the NEW tilt position is stored. Now any additional position movements will restore this new tilt position. This makes is possible with small ON/OFF to change the tilt and with long ON/OFF to change the position and retain the tilt after movement.

Similar to shutterposition there is a minimum runtime of the motor required that TASMOTA can control. This is 0.2sec. Because the tiltmovement from one position into the other takes often about 1sec it is very common that you cannot make small tilt changes of 10° or sometimes even 20".

\ No newline at end of file diff --git a/Bluetooth/index.html b/Bluetooth/index.html new file mode 100644 index 0000000000..a9f0c4afa4 --- /dev/null +++ b/Bluetooth/index.html @@ -0,0 +1,18 @@ + BLE Gateway - Tasmota
Skip to content

BLE Gateway

For ESP32 Bluetooth go here

Bluetooth Low Energy Sensors~

Different vendors offer Bluetooth solutions, mostly as part of the Xiaomi brand, often under the Mijia label. The sensors supported by Tasmota use BLE (Bluetooth Low Energy) to transmit the sensor data, but they differ in their accessibilities quite substantially.

Basically all of them use the so-called "MiBeacons" which are BLE advertisement packets with a certain data structure, which are broadcasted by the devices automatically while the device is not in an active Bluetooth connection.
The frequency of these messages is set by the vendor and ranges from one per 3 seconds to one per hour (f.e. for the battery status of the LYWSD03MMC). Motion sensors and BLE remote controls start to send when an event is triggered.
These packets already contain the sensor data and can be passively received by other devices and will be published regardless if a user decides to read out the sensors via connections or not. Thus the battery life of a BLE sensor is not influenced by reading these advertisements and the big advantage is the power efficiency as no active bi-directional connection has to be established. The other advantage is, that scanning for BLE advertisements can happen nearly parallel (very quickly one after the other), while a direct connection must be established for at least a few seconds and will then block both involved devices for that time.

This is therefore the preferred option, if supported by the sensor.

Supported Devices~

It can not be ruled out, that changes in the device firmware may break the functionality of this driver completely!

The naming conventions in the product range of Bluetooth sensors in Xiaomi universe can be a bit confusing. The exact same sensor can be advertised under slightly different names depending on the seller (Mijia, Xiaomi, Cleargrass, ...).

MJ_HT_V1 LYWSD02 CGG1 CGD1
temperature, humidity, battery temperature, humidity, battery temperature, humidity, battery temperature, humidity, battery
passive for all entities, reliable battery value battery only active, thus not on the NRF24L01, set clock and unit, very frequent data sending passive for all entities, reliable battery value battery only active, thus not on the NRF24L01, no reliable battery value, no clock functions
MiFlora LYWSD03MMC / ATC NLIGHT MJYD2S
temperature, illuminance, soil humidity, soil fertility, battery, firmware version temperature, humidity, battery motion motion, illuminance, battery, no-motion-time
passive only with newer firmware (>3.0?), battery only active, thus not on the NRF24L01 passive only with decryption or using custom ATC-firmware, no reliable battery value with stock firmware NRF24L01, ESP32 passive only with decryption, thus only NRF24L01, ESP32
YEE RC MHO-C401 MHO-C303
button press (single and long) temperature, humidity, battery temperature, humidity, battery
passive equal to the LYWS03MMC, but no custom firmware yet passive for all entities, set clock and unit, no alarm functions, very frequent data sending

passive means data is received via BLE advertisements while active means data is received via a bidirectional connection to the sensor.

LYWSD03MMC sends encrypted sensor data every 10 minutes. As there are no confirmed reports about correct battery presentation of the sensor (always shows 99%), this function is currently not supported.

MJYD2S sends motion detection events and 2 discrete illuminance levels (1 lux or 100 lux for a dark or bright environment). Additionally battery level and contiguous time without motion in discrete growing steps (no motion time = NMT).

Encryption and bind_key~

Most of the older sensors use unencrypted messages, which can be read by all kinds of BLE devices or even a NRF24L01. With the arrival of newer sensors, such as LYWSD03MMC, MHO-C401 or MJYD2S, came the problem of encrypted data in MiBeacons, which can be decrypted in Tasmota (not yet with the HM-1x).

Some sensor still allow an unencrypted connection the reading of the sensor data using normal subscription methods to GATT-services (currently used on the HM-1x). This is more power hungry than the passive reading of BLE advertisements.

Some other sensors like the MJYD2S are not usable without the "bind_key".

It is recommended to obtain the bind_key if usable by your BLE driver to reduce the battery drain.

Obtain bind_key~

To get the necessary decryption key ("bind_key") use:

Telink Flashers allow the generation of a bind_key by faking a pairing with the Xiaomi cloud.

Use bind_key~

Use the bind_key and MAC address of the sensor to use with command NRFkey. Tasmota will receive the sensor data roughly every 10 minutes (in two chunks for humidity and temperature with about a minute in between) and decode the data. This is the most energy efficient way.

The current way of storing these keys on the NRF24L01 is to use NRFkey):

rule1 on System#Boot do backlog NRFkey 00112233445566778899AABBCCDDEEFF112233445566; NRFkey 00112233445566778899AABBCCDDEEFF112233445566; NRFPage 6; NRFUse 0; NRFUse 4 endon
+
(key for two sensors, 6 sensors per page in the WebUI, turn off all sensors, turn on LYWSD03MMC)

Note

This option is currently not available for the HM-10 because of memory considerations as part of the standard sensor-firmware package.

Encryption is not supported on HM-1x (for legacy reasons). The only method is to connect to the sensor from time to time. This circumvents the data encryption but drains the battery fast. Thus it is only recommended as a last resort.

Custom firmware~

pvvx Telink Flasher also allows to flash custom firmware on supported sensors. This will work out of the box with Tasmota. This firmware does send data more frequently and is a bit more power hungry than the stock firmware.

Working principle of Tasmota BLE drivers (>8.5.)~

The idea is to provide drivers with as many automatic functions as possible. Besides the hardware setup, there are zero or very few things to configure.
The sensor namings are based on the original sensor names and shortened if appropriate (Flower care -> Flora). A part of the MAC will be added to the name as a suffix.
All sensors are treated as if they are physically connected to the ESP8266 device. For motion and remote control sensors, MQTT messages will be published in (nearly) real time.

Take note that only the ESP32 and the HM-1x modules are real BLE devices whereas the NRF24L01(+) is only a generic 2.4 GHz transceiver with very limited capabilities that was finagled into reading BLE packets.

using HM-1x~

This feature is included only in tasmota-sensors.bin

Otherwise you must [compile your build]Compile-your-build). Add the following to user_config_override.h:

#ifndef USE_HM10
+#define USE_HM10          // Add support for HM-10 as a BLE-bridge (+9k3 code)
+#endif
+

Features~

Supported sensors will be connected to at a set interval (default interval equals TelePeriod). A subscription is established for 5 seconds and data (e.g. temperature, humidity and battery) is read and reported to an mqtt topic (Dew point is calculated):

tele/%topic%/SENSOR = {"Time":"2020-03-24T12:47:51","LYWSD03-52680f":{"Temperature":21.1,"Humidity":58.0,"DewPoint":12.5,"Battery":100},"LYWSD02-a2fd09":{"Temperature":21.4,"Humidity":57.0,"DewPoint":12.5,"Battery":2},"MJ_HT_V1-d8799d":{"Temperature":21.4,"Humidity":54.6,"DewPoint":11.9},"TempUnit":"C"}
+

After a completed discovery scan, the driver will report the number of found sensors. As Tasmota can not know how many sensors are meant to be discovered you have to force a re-scan until the desired number of devices is found.

Rule1 ON HM10#Found<6 DO Add1 1 ENDON ON Var1#State<=3 DO HM10Scan ENDON 
+
This will re-scan up to 3 times if less than 6 sensors are found.

using nRF24L01(+)~

Configuration~

You must [compile your build]Compile-your-build). Change the following in my_user_config.h:

#define USE_SPI                                  // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC)
+#ifdef USE_SPI
+  #define USE_NRF24                              // Add SPI support for NRF24L01(+) (+2k6 code)
+  #ifdef USE_NRF24
+    #define USE_MIBLE                            // BLE-bridge for some Mijia-BLE-sensors (+4k7 code)
+

Sensors will be discriminated by using the Product ID of the MiBeacon. A human readable short product name will be shown instead of the company-assigned ID of the BLE Public Device Address (the "lower" 24 bits).

A Tele message could like look this:

10:13:38 RSL: stat/tasmota/STATUS8 = {"StatusSNS":{"Time":"2019-12-18T10:13:38","Flora-6ab577":{"Temperature":21.7,"Illuminance":21,"Humidity":0,"Fertility":0},"MJ_HT_V1-3108be":{"Temperature":22.3,"Humidity":56.1},"TempUnit":"C"}}
+

As the NRF24L01 can only read BLE-advertisements, only the data in these advertisements is accessible. All sensors have an additional GATT-interface with more data in it, but it can not be read with a NRF24L01.

As we can not use a checksum to test data integrity of the packet, only data of sensors whose addresses showed up more than once (default = 3 times) will be published. Internally from time to time "fake" sensors will be created, when there was data corruption in the address bytes. These will be removed automatically.

Commands~

Full list of available Bluetooth commands.

iBeacon~

This feature is included only in tasmota-sensors.bin

Otherwise you must compile your build. Add the following to user_config_override.h:

#ifndef USE_IBEACON
+#define USE_IBEACON          // Add support for bluetooth LE passive scan of ibeacon devices 
+#endif
+

HM-1x or nRF24L01(+)~

Tasmota uses a BLE 4.x module to scan for iBeacon devices. This driver is working with HM-10 and clones and HM16/HM17 modules and potentially other from the HM-1x series, depending on firmware capabilities.

Tip

If using an external module, When first connected some modules will be in peripheral mode. You have to change it to central mode using commands Sensor52 1 and Sensor52 2.

Features~

For a list of all available commands see Sensor52 command.

This driver reports all beacons found during a scan with its ID (derived from beacon's MAC address) prefixed with IBEACON_ and RSSI value.

Every beacon report is published as an MQTT tele/%topic%/SENSOR in a separate message:

tele/ibeacon/SENSOR = {"Time":"2021-01-02T12:08:40","IBEACON":{"MAC":"A4C1387FC1E1","RSSI":-56,"STATE":"ON"}}
+

If the beacon can no longer be found during a scan and the timeout interval has passed the beacon's RSSI is set to zero (0) and it is no longer displayed in the webUI

tele/ibeacon/SENSOR = {"Time":"2021-01-02T12:08:40","IBEACON":{"MAC":"A4C1387FC1E1","RSSI":-56,"STATE":"OFF"}}
+

Additional fields will be present depending upon the beacon, e.g. NAME, UID, MAJOR, MINOR.

Supported Devices~

All Apple compatible iBeacon devices should be discoverable.

Various nRF51822 beacons should be fully Apple compatible, programmable and their battery lasts about a year.

Cheap "iTag" beacons with a beeper. The battery on these lasts only about a month.

Tip

You can activate a beacon with a beeper using command IBEACON_%BEACONID%_RSSI 99 (ID is visible in webUI and SENSOR reports). This command can freeze the Bluetooth module and beacon scanning will stop. After a reboot of Tasmota the beacon will start beeping and scanning will resume. (untested on ESP32 native BLE)

\ No newline at end of file diff --git a/Bluetooth_ESP32/index.html b/Bluetooth_ESP32/index.html new file mode 100644 index 0000000000..a60fec5316 --- /dev/null +++ b/Bluetooth_ESP32/index.html @@ -0,0 +1,12 @@ + Bluetooth Low Energy - Tasmota
Skip to content

Bluetooth for ESP32 ~

MI32 Sensors~

This feature is included only in tasmota32-bluetooth.bin

When compiling your build add the following to user_config_override.h:

#define USE_MI_ESP32       // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
+

Different vendors offer Bluetooth solutions, mostly as part of the Xiaomi brand, often under the Mijia label. The sensors supported by Tasmota use BLE (Bluetooth Low Energy) to transmit the sensor data, but they differ in their accessibilities quite substantially.

Basically all of them use the so-called "MiBeacons" which are BLE advertisement packets with a certain data structure, which are broadcasted by the devices automatically while the device is not in an active Bluetooth connection. The frequency of these messages is set by the vendor and ranges from one per 3 seconds to one per hour (f.e. for the battery status of the LYWSD03MMC). Motion sensors and BLE remote controls start to send when an event is triggered. These packets already contain the sensor data and can be passively received by other devices and will be published regardless if a user decides to read out the sensors via connections or not. Thus the battery life of a BLE sensor is not influenced by reading these advertisements and the big advantage is the power efficiency as no active bi-directional connection has to be established. The other advantage is, that scanning for BLE advertisements can happen nearly parallel (very quickly one after the other), while a direct connection must be established for at least a few seconds and will then block both involved devices for that time.

This is therefore the preferred option, if supported by the sensor.

Supported Devices~

It can not be ruled out, that changes in the device firmware may break the functionality of this driver completely!

The naming conventions in the product range of Bluetooth sensors in Xiaomi universe can be a bit confusing. The exact same sensor can be advertised under slightly different names depending on the seller (Mijia, Xiaomi, Cleargrass, ...).

MJ_HT_V1 LYWSD02 CGG1 CGD1
temperature, humidity, battery temperature, humidity, battery temperature, humidity, battery temperature, humidity, battery
passive for all entities, reliable battery value battery only active, thus not on the NRF24L01, set clock and unit, very frequent data sending passive for all entities, reliable battery value battery only active, thus not on the NRF24L01, no reliable battery value, no clock functions
MiFlora LYWSD03MMC / ATC NLIGHT MJYD2S
temperature, illuminance, soil humidity, soil fertility, battery, firmware version temperature, humidity, battery motion motion, illuminance, battery, no-motion-time
passive only with newer firmware (>3.0?), battery only active, thus not on the NRF24L01 passive only with decryption or using custom ATC-firmware, no reliable battery value with stock firmware NRF24L01, ESP32 passive only with decryption, thus only NRF24L01, ESP32
YEE RC MHO-C401 MHO-C303 MCCGQ02HL
button press (single and long) temperature, humidity, battery temperature, humidity, battery door opening/closing, light present
passive equal to the LYWS03MMC, but no custom firmware yet passive for all entities, set clock and unit, no alarm functions, very frequent data sending passive, difficult to get key (must be close, press button)

passive means data is received via BLE advertisements while active means data is received via a bidirectional connection to the sensor.

LYWSD03MMC sends encrypted sensor data every 10 minutes. As there are no confirmed reports about correct battery presentation of the sensor (always shows 99%), this function is currently not supported.

MJYD2S sends motion detection events and 2 discrete illuminance levels (1 lux or 100 lux for a dark or bright environment). Additionally battery level and contiguous time without motion in discrete growing steps (no motion time = NMT).

Encryption and bind_key~

Most of the older sensors use unencrypted messages, which can be read by all kinds of BLE devices or even a NRF24L01. With the arrival of newer sensors, such as LYWSD03MMC, MHO-C401 or MJYD2S, came the problem of encrypted data in MiBeacons, which can be decrypted in Tasmota.

Some sensor still allow an unencrypted connection the reading of the sensor data using normal subscription methods to GATT-services. This is more power hungry than the passive reading of BLE advertisements.

Some other sensors like the MJYD2S are not usable without the "bind_key".

It is recommended to obtain the bind_key in any case to reduce the battery drain.

Obtain bind_key~

To get the necessary decryption key ("bind_key") use:

Telink Flashers allow the generation of a bind_key by faking a pairing with the Xiaomi cloud.

Use bind_key~

Use the bind_key and MAC address of the sensor to use with commands Mi32Keys or Mi32Key. Tasmota will receive the sensor data roughly every 10 minutes (in two chunks for humidity and temperature with about a minute in between) and decode the data. This is the most energy efficient way.

The current way of storing these keys on the ESP32 is to use Mi32Key command:

MI32Keys <mac or blealias>=<bind_key> <mac or blealias>=<key>

The Key is the 32 character (16 byte) key retrieved by above methods.

Older way of using MI32Key is retained for backward compatibility. Mi32Key needs a 44 character combination of bind_key and MAC:

MI32Keys <bind_keymac>

To retain the data use a rule on startup:

rule1 on System#Boot do backlog MI32key 00112233445566778899AABBCCDDEEFF112233445566; MI32key 00112233445566778899AABBCCDDEEFFAABBCCDDEEFF endon
+

Commands~

Full list of available Mi Sensors commands.

Tip

If you really want to read battery for LYWSD02, Flora and CGD1, consider doing it once a day with a rule:

Backlog Rule1 on Time#Minute=30 do MI32Battery endon; Rule1 1
+

This will update every day at 00:30 AM.

BLE ESP32~

This allows for the receiving of BLE advertisements from BLE devices, including "iBeacons" and BLE sensors, but also for the control of simple BLE devices, providing for reading, writing and receiving notifications.

This feature is included only in tasmota32-bluetooth.bin

When compiling your build add the following to user_config_override.h:

#define USE_BLE_ESP32                // Add support for ESP32 as a BLE-bridge (+9k2? mem, +292k? flash)
+

Be aware, enabling of the native BLE on ESP32 has an impact on Wi-Fi performance. Although later SDK helped a bit, expect more lag on the web interface and on MQTT. If only controlling BLE devices, then scanning can be disabled, which will minimize Wi-Fi impact. BLE can be enabled from the web UI menus.

This is compiled by default in the Bluetooth firmware, but you need to enable it using the webUI Configure BLE button or SetOption115 1 command.

Note that the only configuration stored is the SetOption115 to turn BLE on and off. All other configurations can be set at boot if necessary using Rules.

iBeacon~

Hear adverts from BLE devices, and produce MQTT messages containing RSSI and other information about them. Break out iBeacon specific data if present.

This feature is not included in precompiled binaries

When compiling your build add the following to user_config_override.h:

#define USE_IBEACON_ESP32                // Add support for Bluetooth LE passive scan of iBeacon devices
+

This driver reports all beacons found during a scan with its ID (derived from beacon's MAC address) prefixed with IBEACON_ and RSSI value.

Every beacon report is published as an MQTT tele/%topic%/SENSOR in a separate message:

tele/ibeacon/SENSOR = {"Time":"2021-01-02T12:08:40","IBEACON":{"MAC":"A4C1387FC1E1","RSSI":-56,"STATE":"ON"}}
+

If the beacon can no longer be found during a scan and the timeout interval has passed the beacon's RSSI is set to zero (0) and it is no longer displayed in the webUI

tele/ibeacon/SENSOR = {"Time":"2021-01-02T12:08:40","IBEACON":{"MAC":"A4C1387FC1E1","RSSI":-56,"STATE":"OFF"}}
+

Additional fields will be present depending upon the beacon, e.g. NAME, UID, MAJOR, MINOR.

iBeacon MQTT Fields~

Always present~

json meaning
Time time of MQTT send
IBEACON.MAC mac addr
IBEACON.RSSI signal strength
IBEACON.STATE ON - present, OFF - last MQTT you will get for now (device removed)

Optional~

json meaning
IBEACON.NAME name if in scan, or BLEAlias if set - only present if NAME present
IBEACON.PERSEC count of adverts per sec. Useful for detecting button press
IBEACON.MAJOR some iBeacon related term? - only present for some
IBEACON.MINOR some iBeacon related term? - only present for some

Examples~

Setup a rule to set some aliases at boot time, and only allow those starting iB~

Rule1 ON System#Boot DO backlog iBeacon 1; BLEAlias A4C1386A1E24=iBfred A4C1387FC1E1=iBjames; iBeaconOnlyAliased 2 endon
+Rule1 1
+

Supported Devices~

All Apple compatible iBeacon devices should be discoverable.

Various nRF51822 beacons should be fully Apple compatible, programmable and their battery lasts about a year.

Cheap "iTag" beacons with a beeper. The battery on these lasts only about a month.

Tip

You can activate a beacon with a beeper using command IBEACON_%BEACONID%_RSSI 99 (ID is visible in webUI and SENSOR reports). This command can freeze the Bluetooth module and beacon scanning will stop. After a reboot of Tasmota the beacon will start beeping and scanning will resume. (untested on ESP32 native BLE)

MI32 MQTT Messages~

Because we can have many sensors reporting, tele messages are chunked to have a maximum of four sensors per message.

If you enable MQTT discovery (SetOption19 1), additional MQTT messages are send.

Primarily, at teleperiod or MI32period, discovery messages are sent. These inform Home Assistant about the devices. Device names can be dependent upon BLEAlias, so set BLEAlias at boot.

With Mi32Option6, additional actual data messages are sent on a dedicated topic (set by Mi32Topic) topics including the device name: tele/<mi32topic>/<name>

Each message for one sensor.

These messages can be used without Home Assistant if it is a preferred format.

Note

The topic would be the same from all Tasmotas if they have the same BLEAlias or no BLEAlias. So if you wish to 'hear' the same device separately from different Tasmotas, use different BLEAlias names or different Mi32Topic.

EQ3 TRV~

A preliminary EQ3 driver is in production. Documentation.

Commands~

Full list of available BLE ESP32 commands.

If you enable MQTT discovery (SetOption19 1), additional MQTT messages are send.

Primarily, at teleperiod or MI32period, discovery messages are sent. These inform Home Assistant about the devices. Device names can be dependent upon BLEAlias, so set BLEAlias at boot.

Backlog Rule1 ON System#Boot DO BLEAlias A4C1386A1E24=fred A4C1387FC1E1=james endon; Rule1 1
+

Enable static random mac addresses in addition to public mac addresses~

If you have a device which does not appear, it may be advertising on a 'static random' address. Using this command may make it appear, but will also make other devices appear that you may not wish to see... For example phones advertising Covid messages. To know more, google 'BLE static random'. Be aware you can set 2 or 3....

Note

The topic would be the same from all Tasmotas if they have the same BLEAlias or no BLEAlias. So if you wish to 'hear' the same device separately from different Tasmotas, use different BLEAlias names.

Setup a rule to set some aliases at boot time~

Backlog Rule1 ON System#Boot DO BLEAlias A4C1386A1E24=fred A4C1387FC1E1=james endon; Rule1 1
+

Enable static random mac addresses in addition to public mac addresses~

BLEAddrFilter 1

Check the interval between BLE tele messages~

BLEPeriod

Set it to 40s: BLEPeriod 40

\ No newline at end of file diff --git a/Bluetooth_MI32/index.html b/Bluetooth_MI32/index.html new file mode 100644 index 0000000000..6a61c6a1b0 --- /dev/null +++ b/Bluetooth_MI32/index.html @@ -0,0 +1,364 @@ + MI32 legacy - Tasmota
Skip to content

MI32 legacy~

The MI32-driver focuses on the passive observation of BLE sensors of the Xiaomi/Mijia universe, thus only needing a small memory footprint. This allows to additionally run a dedicated software bridge to Apples HomeKit with very few additional configuration steps. Berry can be used to create generic applications.
Currently supported are the original ESP32, the ESP32-C3 and the ESP32-S3.

Usage~

This driver is not part of any standard build. To self compile it is recommended to add build environments to platformio_tasmota_cenv.ini. This file needs to be created first.
Add these sections:

[env:tasmota32-mi32-homebridge]
+extends                 = env:tasmota32_base
+build_flags             = ${env:tasmota32_base.build_flags}
+                          -DUSE_MI_ESP32
+                          -DUSE_MI_EXT_GUI
+                          -DUSE_MI_HOMEKIT=1    ; 1 to enable; 0 to disable
+lib_extra_dirs          = lib/libesp32, lib/libesp32_div, lib/lib_basic, lib/lib_i2c, lib/lib_div, lib/lib_ssl
+lib_ignore              = ESP8266Audio
+                          ESP8266SAM
+                          TTGO TWatch Library
+                          Micro-RTSP
+                          epdiy
+                          esp32-camera
+
+[env:tasmota32c3-mi32-homebridge]
+extends                 = env:tasmota32c3
+build_flags             = ${env:tasmota32_base.build_flags}
+                          -DUSE_MI_ESP32
+                          -DUSE_MI_EXT_GUI
+                          -DUSE_MI_HOMEKIT=1    ; 1 to enable; 0 to disable
+lib_extra_dirs          = lib/libesp32, lib/libesp32_div, lib/lib_basic, lib/lib_i2c, lib/lib_div, lib/lib_ssl
+lib_ignore              = ESP8266Audio
+                          ESP8266SAM
+                          TTGO TWatch Library
+                          Micro-RTSP
+                          epdiy
+                          esp32-camera
+

It is probably necessary to restart your IDE (i.e. Visual Studio Code) to see the option to build these environments.

Tasmota and BLE-sensors~

Different vendors offer Bluetooth solutions as part of the XIAOMI family often under the MIJIA-brand (while AQUARA is the typical name for a ZigBee sensor).
The sensors supported by Tasmota use BLE (Bluetooth Low Energy) to transmit the sensor data, but they differ in their accessibilities quite substantially.

Basically all of them use of so-called „MiBeacons“ which are BLE advertisement packets with a certain data structure, which are broadcasted by the devices automatically while the device is not in an active bluetooth connection.
The frequency of these messages is set by the vendor and ranges from one per 3 seconds to one per hour (for the battery status of the LYWSD03MMC). Motion sensors and BLE remote controls start to send when an event is triggered.
These packets already contain the sensor data and can be passively received by other devices and will be published regardless if a user decides to read out the sensors via connections or not. Thus the battery life of a BLE sensor is not influenced by reading these advertisements and the big advantage is the power efficiency as no active bi-directional connection has to be established. The other advantage is, that scanning for BLE advertisements can happen nearly parallel (= very quick one after the other), while a direct connection must be established for at least a few seconds and will then block both involved devices for that time.
This is therefore the preferred option, if technically possible (= for the supported sensors).

Most of the „older“ BLE-sensor-devices use unencrypted messages, which can be read by all kinds of BLE-devices or even a NRF24L01. With the arrival of "newer" sensors came the problem of encrypted data in MiBeacons, which can be decrypted in Tasmota.
Meanwhile it is possible to get the needed "bind_key" without the need to use Xiaomis apps and server infrastructure.
At least the LYWSD03 allows the use of a simple BLE connection without any encrypted authentication and the reading of the sensor data using normal subscription methods to GATT-services (currently used on the HM-1x). This is more power hungry than the passive reading of BLE advertisements. It is not directly supported by the driver anymore, but can be realized in Berry, i.e. to read the correct battery status.
Other sensors like the MJYD2S and nearly every newer device are not usable without the "bind_key".

The idea is to provide as many automatic functions as possible. Besides the hardware setup, there are zero or very few things to configure.
The sensor namings are based on the original sensor names and shortened if appropriate (Flower care -> Flora). A part of the MAC will be added to the name as a suffix.
All sensors are treated as if they are physically connected to the ESP32 device. For motion and remote control sensors MQTT-messages will be published in (nearly) real time.

Supported Devices~

It can not be ruled out, that changes in the device firmware may break the functionality of this driver completely!

The naming conventions in the product range of bluetooth sensors in XIAOMI-universe can be a bit confusing. The exact same sensor can be advertised under slightly different names depending on the seller (Mijia, Xiaomi, Cleargrass, ...).

MJ_HT_V1 LYWSD02 CGG1 CGD1
temperature, humidity, battery temperature, humidity, battery temperature, humidity, battery temperature, humidity, battery
passive for all entities, reliable battery value battery only active, set clock and unit, very frequent data sending passive for all entities, reliable battery value battery only active, no reliable battery value, no clock functions
MiFlora LYWSD03MMC / ATC NLIGHT MJYD2S
temperature, illuminance, soil humidity, soil fertility, battery, firmware version temperature, humidity, battery motion motion, illuminance, battery, no-motion-time
passive only with newer firmware (>3.0?), battery only active passive only with decryption or using custom ATC-firmware, no reliable battery value with stock firmware passive passive only with decryption
YLYK01 MHO-C401 MHO-C303 MCCGQ02HL
button press (single and long) temperature, humidity, battery temperature, humidity, battery contact opening/closing, battery
passive equal to the LYWS03MMC passive for all entities, set clock and unit, no alarm functions, very frequent data sending passive only with decryption
SJWS01L YLKG07/08
button press (single and long), leak alarm, battery button press (single and double), hold,
incremental rotary encoder w/o press
passive only with decryption passive only with decryption (legacy decryption)
both versions reported as YLKG08

passive: data is received via BLE advertisements active: data is received via bidrectional connection to the sensor

Devices with payload encryption~

The encrypting devices will start to send advertisements with encrypted sensor data after pairing it with the official Xiaomi app. Out-of-the-box the sensors do only publish a static advertisement.
It is possible to do a pairing and get the necessary decryption key ("bind_key") right here in the Wiki. This method uses the same code base as the first published working example: https://atc1441.github.io/TelinkFlasher.html. This project also provides a custom firmware for the LYWSD03MMC, which then becomes an ATC and is supported by Tasmota too. Default ATC-setting will drain the battery more than stock firmware, because of very frequent data sending.
This key and the corresponding MAC of the sensor can be injected with the MI32key-command (or NRFMJYD2S), but the new and recommended option is to use a mi32cfg file.

It is still possible to save the whole config as RULE like that: (not recommended anymore!)

rule1 on System#Boot do backlog MI32key 00112233445566778899AABBCCDDEEFF112233445566; MI32key 00112233445566778899AABBCCDDEEFF112233445566 endon
+
(key for two sensors)

MI32 Configuration~

There are several ways to manage and save your configuration.

  1. Do it on the device
    Starting after a fresh install the driver will not find a configuration file and begins to search for every BLE device, that it can unterstand. Thus after a while all BLE sensors in sight should have been added to the non-persistent internal memory.
    You can save them with command MI32CFG, which will create a JSON-file named 'mi32cfg' in the root folder of the internal flash of the ESP32. After the next reboot, the driver will read this configuration into memory and does not add more devices to this list.

  2. Create the mi32cfg file manually
    After a fresh install you can simply create a file in the root folder of the flash file system with the name 'mi32cfg' and paste the JSON into it. Save it and reboot.

  3. Adding sensors including the keys directly on this page
    It is recommended to paste the data of 'mi32cfg' into the next textfield, if you already have one. After that press IMPORT MI32CFG. The config will get parsed and presented in a table.

MI32CFG Importer - Web App~

MI32CFG - nothing imported yet


empty config

[]
+

After that you can add more sensors with the following Bind Key Generator, which will also add sensors, that do not need a key. This will only work, if your browser supports this and should work with Opera, Chrome and Edge. Safari and Firefox are not able to do this.
After successful pairing a sensor in the next step or simply connecting to a non-encrypting sensor, the JSON in the textfield above will be updated with the added new sensor at the bottom. You can copy-paste the new JSON via the Web-GUI to the mi32cfg file on the ESP32 or save it elsewhere. For adding more sensors, repeat the whole procedure after refreshing the site (after saving your data!!).

Bind Key Generator - Web App~

99% of the work was done by: https://atc1441.github.io/TelinkFlasher.html !!

Will disable device in MiHome

When doing an activation here the device is needed to be activated in the Mi app again when wanted to use there.



Device id:  Mi Token:
Mi Bind Key: MAC:


BLE-Log:

.

Commands~

Command Parameters
MI32Cfg Saves current sensor list as mi32cfg file to the flash of the ESP32. After reboot only the saved drivers will be observed, no unknown drivers will be added. A valid config file is mandatory for HomeKit in this driver.
MI32Key Set a "bind_key" for a MAC-address to decrypt sensor data (LYWSD03MMC, MJYD2S). The argument is a 44 uppercase characters long string, which is the concatenation of the bind_key and the corresponding MAC.
<00112233445566778899AABBCCDDEEFF> (32 characters) = bind_key
<112233445566> (12 characters) = MAC of the sensor
<00112233445566778899AABBCCDDEEFF112233445566> (44 characters)= final string
MI32Option0 0 = sends only recently received sensor data
1 = aggregates all recent sensors data types
MI32Option1 0 = shows full sensor data at Teleperiod
1 = shows no sensor data at Teleperiod
MI32Option2 0 = sensor data only at Teleperiod (default)
1 = direct bridging of BLE data to MQTT messages
MI32Option3 0 = do not add new sensors, which is set after a valid mi32cfg file is parsed after boot (default)
1 = turn on auto-adding of new sensors again
MI32Option4 0 = use passive scanning (default)
1 = use active scanning, needed for a few sensors (can have negative side effects!)

Tip

If you really want to read battery for LYWSD02, Flora and CGD1, consider doing it in Berry.

Tip

If you want to add a new BLE sensor to your config on the device, use MI32option3 1 to add the new sensor by catching a BLE packet. Then use MI32Cfg to save the new config on flash.

Mi Dashboard~

The driver provides an extended web GUI to show the observed Xiaomi sensors in a widget style, that features a responsive design to use the screen area as effective as possible. The other advantage is, that only the widget with new data gets redrawn (indicated by a fading circle) and no unnecessary refresh operations will happen. A simple graph shows if valid data for every hour was received in the last 24h, where only one gap for the coming hour is not a sign of an error. Configured sensors with no received packet since boot or key/decryption errors are dimmed.

HomeKit Bridge~

If activated at compile time the driver will start the HAP core (= the main task of the HomeKit framework) after successfully reading a valid mi32cfg file after the start. It will create a 'bridge accessory' presenting all configured BLE devices to HomeKit. You can add the ESP32 as such a Mi-Home-Bridge to HomeKit in the native way, like you would add a commercial product to you local HomeKit network. The setup key is derived from the Wifi MAC of your ESP32 to easily allow many ESP32 to be used as a HomeKit bridge in your local network. Besides the driver will also manage up to four relays and sync them with HomeKit.
There is nothing more to configure, the driver will automatically translate the data packets back and forth.
It just works ... except, when it does not.

Known issues

The underlying HAP framework will expose incompatible network configurations, which will likely be related to mDNS and IGMP settings. There is nothing, that the Tasmota side can fix here.

If it is not broken, do not upgrade

Although the driver does not write to the NVS section and the usage of a modified HAP framework (using a NVS wrapper) the behavior of Tasmota firmware upgrades is undefined with regards to a working HomeKit installation. Similar things can also happen using much bigger projects like "Homebridge". So it might be necessary to completely erase the Mi-Bridge from your "Home" in Homekit and doing a flash erase of the ESP32 after a firmware upgrade of the ESP32. It is recommend to first create a final mi32cfg file before you add the Mi-Home-Bridge to your "Home".

HomeKit QR-Code-Generator - Web App~

This will generate a QR-Code based on the MAC address of the ESP32 which runs Tasmotas Homekit-Bridge. Use the camera of your iPhone or iPad to easily start the setup procedure.


Homeassistant and Tasmota - BLE sensors~

After creating a valid configuration with a mi32cfg file in the local file system, it is possible to announce all sensors to Homeassistant via MQTT discovery by using a Berry script. This will parse the mi32cfg file and create all needed entities for Homassistant by publishing specific messages to Homeassistant.
It will not generate duplicated sensors, but instead allows to use multiple ESP's as data sources for the same BLE sensor. The best way is to not fiddle around with the default Tasmota configuration, especially not to change the default topic name, because this will lose the ability to automatically configure everything.

One way to use it, is to save the following script disco.be to the filesystem of the ESP and the launch it at the startup.
Create autoexec.bat if not already present and add the following line:
br load("disco")
This will create and/or init entities for every sensor and group them as a single device for every BLE device in Homeassistants MQTT integration.

In the diagnostic panel of every sensor you will see the signal strength of the BLE sensor in relation to the observing ESP, so the value will very likely differ between multiple of these BLE-ESP32-combinations. A virtual Tasmota BLE Hub device is created, that shows all contributing ESP32 nodes for a better overview.

For sensors like humidity or temperature it should not matter, how many ESP's do contribute data. For buttons of a remote control or binary sensors like motion, this could have side effects, as multiple events will be generated (in a very short time frame). The dimmer of the YLKG08 is special case, as the data of the BLE sensor are relative steps, that are combined to a so called numberentity with a range of 0 - 100. That way multiple messages from many ESP's will add up and "accelerate" the dimmer knob.

Tip

Use the embedded MI32CFG Importer on this site to delete unwanted sensors and then save the result to the ESP32 of your choice.

Berry support~

The driver provides two Berry modules to allow extensions and interactions with the sensors. It is also possible to write generic BLE functions unrelated to Xiaomi sensors.

MI32 module~

This module allows access and modification of the internal data backend of the MI32 driver for the observed Xiaomi sensors.
First we need to import the module:
import MI32

We have the following methods, which are chosen to be able to replace the old commands MI32Battery, MI32Unit and MI32Time:

  • MI32.devices(): returns the number of monitored Xiaomi devices
  • MI32.get_name(x): returns a string with the sensor name (internal name of the driver) at slot x in the internal sensor array of the driver
  • MI32.get_MAC(x): returns a byte buffer (6 bytes) representing the MAC at slot x in the internal sensor array of the driver
  • MI32.set_bat(x,v): sets the battery value to v at slot x in the internal sensor array of the driver
  • MI32.set_hum(x,v): sets the humidity value to v at slot x in the internal sensor array of the driver
  • MI32.set_temp(x,v): sets the temperature value to v at slot x in the internal sensor array of the driver

BLE module~

For generic BLE access we import the module:
import BLE

To simplify BLE access this works in the form of state machine, where you have to set some properties of a context and then finally launch an operation. Besides we have three callback mechanisms for listening to advertisements, active sensor connections with Tasmota as a client and providing a server including advertising. All need a byte buffer in Berry for data exchange and a Berry function as the callback.
The byte buffer is always organized in the format length-data bytes, where the first byte represents the length of the following data bytes, which results in a maximum of 255 data bytes.

To listen to advertisements inside a class (that could be a driver) we could initialize like that:

Simple Advertisement Listener

var buf
+def init()
+    import BLE
+    self.buf = bytes(-64)
+    var cbp = tasmota.gen_cb(/s,m-> self.cb(s,m))
+    BLE.adv_cb(cbp,self.buf)
+end
+
+def cb(svc,manu)
+  print(buf) # simply prints out the byte buffer of an advertisement packet
+  if svc != 0 # if service data present
+      print("service data:")
+      var _len = self.buf[svc-2]-1
+      # the index points to the data part of an AD element, two position before that is length of "type + data", 
+      # so we subtract one byte from that length to get the "pure" data length
+      print(self.buf[svc.._len+svc])
+  end
+  if manu != 0 # if manufacturer data present
+      print("manufacturer data:")
+      var _len = self.buf[manu-2]-1
+      print(self.buf[manu.._len+manu])
+  end
+end
+

To stop listening call:
BLE.adv_cb(nil)

We just have to provide a pointer to a (callback) function and a byte buffer. The returned data in the byte buffer uses the following proprietary format:

6 bytes - MAC
+1 byte - address type 
+1 byte  - RSSI
+1 byte  - length of payload
+n bytes - payload data 
+

The advertisement callback function provides 2 arguments, which are indices of the whole buffer that point to optional parts of the payload. A value of 0 means, this type of of element is not in the payload.
1. svc (= service data index) - index of service data in the advertisement buffer 2. manu (= manufacturer data index) - index of manufacturer data in the advertisement buffer

The payload is always provided completely, so every possibles AD type can be parsed in Berry if needed, but for convenience the two most important types for IOT applications are given in the callback.

Tip

The payload can be parsed according to the BLE GAP standard. It consists of AD elements of variable size in the format length-type-data, where the length byte describes the length of the two following components in bytes, the type byte is defined in the GAP and the data parts of 'length-1' bytes is interpreted according to the type.

Two methods for filtering of advertisements are provided:
BLE.adv_watch(MAC,type): watch BLE address exclusively, is added to a list (MAC is a 6-byte-buffer, type is optional 0-3, default is 0)
BLE.adv_block(MAC,type): block BLE address, is added to a list(MAC is a 6-byte-buffer, type is optional 0-3, default is 0)

Tip

The watchlist is more effective to avoid missing packets, than the blocklist in environments with high BLE traffic. Both methods work for the internal Xiaomi driver and the post processing with Berry.

Communicating via connections is a bit more complex. We have to start with a callback function and a byte buffer again.

# simple example for the Berry console
+import BLE
+cbuf = bytes(-64)
+
+def cb(error,op,uuid)
+end
+
+cbp = tasmota.gen_cb(cb)
+BLE.conn_cb(cbp,cbuf)
+

Error codes:~

  • 0 - no error
  • 1 - connection error
  • 2 - did disconnect
  • 3 - did not get service
  • 4 - did not get characteristic
  • 5 - could not read value
  • 6 - characteristic can not notify
  • 7 - characteristic not writable
  • 8 - did not write value
  • 9 - timeout: did not read on notify

Op codes:~

  • 1 - read
  • 2 - write
  • 3 - subscribe - direct response after launching a run command to subscribe
  • 103 - notify read - the notification with data from the BLE server

UUID:
Returns the 16 bit UUID of the characteristic as a number, that returns a value.

Internally this creates a context, that can be modified with the following methods:

Set the MAC of the device we want to connect to:
BLE.set_MAC(MAC,type): where MAC is a 6-byte-buffer, type is optional 0-3, default is 0

Set service and characteristic:
BLE.set_svc(string): where string is a 16-Bit, 32-Bit or 128-Bit service uuid
BLE.set_chr(string): where string is a 16-Bit, 32-Bit or 128-Bit characteristic uuid

Finally run the context with the specified properties and (if you want to get data back to Berry) have everything prepared in the callback function:
BLE.run(operation,response): where operation is a number (optional: boolean w/o server response to write or subscribe, default is false) , that represents an operation in a proprietary format. Values below 10 will not disconnect automatically after completion:

  • 1 - read
  • 2 - write
  • 3 - subscribe
  • 5 - disconnect

  • 11 - read - then disconnect (returns 1 in the callback)

  • 12 - write - then disconnect (returns 2 in the callback)
  • 13 - subscribe - then disconnect after waiting for notification(returns 3 in the callback)

The buffer format for reading and writing is in the format (length - data):

1 byte  - length of data in bytes
+n bytes - data
+

The server is initiated similarly with BLE.serv_cb(cbp,cbuf). After that you have to construct the server by first adding all characteristics and finally starting it, by setting the advertisement data for the first time. Setting advertisement data without adding characteristics will not start a BLE server but only a BLE advertiser, which is totally fine for some use cases (i.e. Beacons, BTHome).
The BLE server can be stopped with BLE.serv_cb(nil), which will restart the "BLE Scan Task".

The callback functions returns error, operation, 16-bit-uuid and 16-bit-handle.

# simple server setup example for the Berry console
+import BLE
+cbuf = bytes(-256)
+
+def cb(error,op,uuid,handle)
+end
+
+cbp = tasmota.gen_cb(cb)
+BLE.serv_cb(cbp,cbuf)
+# now add characteristics and advertisement ...
+

Command op codes:~

  • 201 - add and/or set advertisement data according to the BLE standard, typically chaining small packets in the in the format of length-type-data. When called for the first time, will return a bytes buffer, that represents an array of the 16-bit-handles of all characteristics in the order of addition.
  • 202 - add and/or set scan response data, according to the BLE standard which is equally to the advertisement data. Should be used sparsely.
  • 211 - add and/or set characteristic with value of bytes buffer. For simplicity a Client Characteristic Configuration Descriptor (aka 0x2902) will be added on construction of every characteristic and read, write, notification and indication is enabled. You can select write with or without response withe the optional boolean of BLE.set_chr(string, bool), which defaults to "write with no response". The function call will always trigger a notification. As every characteristic belongs to a service, BLE.set_svc(string) must have been called before.

Response op codes (triggered when a client interacts with the server):~

  • 221 - on read of a characteristic, returns no buffer data
  • 222 - on write of a characteristic, returns the written values as byte buffer data
  • 223 - on unsubscribe, returns no buffer data
  • 224 - on subscribe to notifications, returns no buffer data
  • 225 - on subscribe to indications, returns no buffer data
  • 226 - on subscribe to notifications and indications, returns no buffer data
  • 227 - on connect, returns MAC of client device as byte buffer
  • 228 - on disconnect, returns no buffer data
  • 229 - on status, returns error code as byte buffer

Berry examples~

Here is an implementation of the "old" MI32 commands:

removed MI32 commands in Berry

import BLE
+import MI32
+
+j = 0
+sl = 0
+
+cbuf = bytes(-64)
+def cb()
+
+    if j == 0
+        print(cbuf)
+    end
+    if j == 1
+        var temp = cbuf.get(1,2)/100.0
+        var hum = cbuf.get(3,1)*1.0
+        var bat = (cbuf.get(4,2)-2100)/12
+        MI32.set_temp(sl,temp)
+        MI32.set_hum(sl,hum)
+        MI32.set_bat(sl,bat)
+    end
+    if j == 4
+        var bat = cbuf.get(1,1)
+        MI32.set_bat(sl,bat)
+    end
+end
+
+cbp = tasmota.gen_cb(cb)
+BLE.conn_cb(cbp,cbuf)
+
+def SetMACfromSlot(slot)
+    if slot+1>MI32.devices()
+        return "out of bounds"
+    end
+    sl = slot
+    BLE.set_MAC(MI32.get_MAC(slot))
+end
+
+def MI32Time(slot)
+    SetMACfromSlot(slot)
+    BLE.set_svc("EBE0CCB0-7A0A-4B0C-8A1A-6FF2997DA3A6")
+    BLE.set_chr("EBE0CCB7-7A0A-4B0C-8A1A-6FF2997DA3A6")
+    cbuf[0] = 5
+    var t = tasmota.rtc()
+    var utc = t.item("utc")
+    var tz = t.item("timezone")/60
+    cbuf.set(1,utc,4)
+    cbuf.set(5,tz,1)
+    j = 0
+    BLE.run(12,true)
+end
+
+def MI32Unit(slot,unit)
+    SetMACfromSlot(slot)
+    BLE.set_svc("EBE0CCB0-7A0A-4B0C-8A1A-6FF2997DA3A6")
+    BLE.set_chr("EBE0CCBE-7A0A-4B0C-8A1A-6FF2997DA3A6")
+    cbuf[0] = 1
+    cbuf[1] = unit
+    j = 0
+    BLE.run(12,true)
+end
+
+def MI32Bat(slot)
+    SetMACfromSlot(slot)
+    var name = MI32.get_name(slot)
+    if name == "LYWSD03"
+        BLE.set_svc("ebe0ccb0-7A0A-4B0C-8A1A-6FF2997DA3A6")
+        BLE.set_chr("ebe0ccc1-7A0A-4B0C-8A1A-6FF2997DA3A6")
+        j = 1
+        BLE.run(13)
+    end
+    if name == "MHOC401"
+        BLE.set_svc("ebe0ccb0-7A0A-4B0C-8A1A-6FF2997DA3A6")
+        BLE.set_chr("ebe0ccc1-7A0A-4B0C-8A1A-6FF2997DA3A6")
+        j = 1
+        BLE.run(13,true)
+    end
+    if name == "LYWSD02"
+        BLE.set_svc("ebe0ccb0-7A0A-4B0C-8A1A-6FF2997DA3A6")
+        BLE.set_chr("ebe0ccc1-7A0A-4B0C-8A1A-6FF2997DA3A6")
+        j = 2
+        BLE.run(11,true)
+    end
+    if name == "FLORA"
+        BLE.set_svc("00001204-0000-1000-8000-00805f9b34fb")
+        BLE.set_chr("00001a02-0000-1000-8000-00805f9b34fb")
+        j = 3
+        BLE.run(11,true)
+    end
+    if name == "CGD1"
+        BLE.set_svc("180F")
+        BLE.set_chr("2A19")
+        j = 4
+        BLE.run(11,true)
+    end
+end
+

More Examples~

Govee desk lamp - pre-alpha
# control a BLE Govee desk lamp
+class GOVEE : Driver
+    var buf
+
+    def init(MAC)
+        import BLE
+        self.buf = bytes(-21) # create a byte buffer, first byte reserved for length info
+        self.buf[0] = 20 # length of the data part of the buffer in bytes
+        self.buf[1] = 0x33 # a magic number - control byte for the Govee lamp
+        var cbp = tasmota.gen_cb(/e,o,u->self.cb(e,o,u)) # create a callback function pointer
+        BLE.conn_cb(cbp,self.buf)
+        BLE.set_MAC(bytes(MAC),1) # addrType: 1 (random)
+    end
+
+    def cb(error,op,uuid)
+        if error == 0
+            print("success!")
+            return
+        end
+        print(error)
+    end
+
+    def chksum()
+        var cs = 0
+        for i:1..19
+            cs ^= self.buf[i]
+        end
+        self.buf[20] = cs
+    end
+
+    def clr()
+        for i:2..19
+            self.buf[i] = 0
+        end
+    end
+
+    def writeBuf()
+        import BLE
+        BLE.set_svc("00010203-0405-0607-0809-0a0b0c0d1910")
+        BLE.set_chr("00010203-0405-0607-0809-0a0b0c0d2b11")
+        self.chksum()
+        print(self.buf)
+        BLE.run(12) # op: 12 (write, then disconnect)
+    end
+end
+
+gv = GOVEE("AABBCCDDEEFF") # MAC of the lamp
+tasmota.add_driver(gv)
+
+def gv_power(cmd, idx, payload, payload_json)
+    if int(payload) > 1
+        return 'error'
+    end
+    gv.clr()
+    gv.buf[2] = 1 # power cmd
+    gv.buf[3] = int(payload)
+    gv.writeBuf()
+end
+
+def gv_bright(cmd, idx, payload, payload_json)
+    if int(payload) > 255
+        return 'error'
+    end
+    gv.clr()
+    gv.buf[2] = 4 # brightness
+    gv.buf[3] = int(payload)
+    gv.writeBuf()
+end
+
+def gv_rgb(cmd, idx, payload, payload_json)
+    var rgb = bytes(payload)
+    print(rgb)
+    gv.clr()
+    gv.buf[2] = 5 # color
+    gv.buf[3] = 5 # manual ??
+    gv.buf[4] = rgb[3]
+    gv.buf[5] = rgb[0]
+    gv.buf[6] = rgb[1]
+    gv.buf[7] = rgb[2]
+    gv.writeBuf()
+end
+
+def gv_scn(cmd, idx, payload, payload_json)
+    gv.clr()
+    gv.buf[2] = 5 # color
+    gv.buf[3] = 4 # scene
+    gv.buf[4] = int(payload)
+    gv.writeBuf()
+end
+
+def gv_mus(cmd, idx, payload, payload_json)
+    var rgb = bytes(payload)
+    print(rgb)
+    gv.clr()
+    gv.buf[2] = 5 # color
+    gv.buf[3] = 1 # music
+    gv.buf[4] = rgb[0]
+    gv.buf[5] = 0
+    gv.buf[6] = rgb[1]
+    gv.buf[7] = rgb[2]
+    gv.buf[8] = rgb[3]
+    gv.writeBuf()
+end
+
+
+tasmota.add_cmd('gpower', gv_power) # only on/off
+tasmota.add_cmd('bright', gv_bright) # brightness 0 - 255
+tasmota.add_cmd('color', gv_rgb) #  color 00FF0000 - sometimes the last byte has to be set to something greater 00, usually it should be 00
+tasmota.add_cmd('scene', gv_scn) # scene 0 - ?,
+tasmota.add_cmd('music', gv_mus) # music 00 - 0f + color 000000   -- does not work at all!!!
+
+#   POWER      = 0x01
+#   BRIGHTNESS = 0x04
+
+#   COLOR      = 0x05
+    #   MANUAL     = 0x02 - seems to be wrong for this lamp
+    #   MICROPHONE = 0x01 - can not be confirmed yet
+    #   SCENES     = 0x04
+
Xbox X/S controller - proof of concept
# just a proof of concept to connect a Xbox X/S controller
+# must be repaired on every connect
+class XBOX : Driver
+    var buf
+
+    def init(MAC)
+        import BLE
+        var cbp = tasmota.gen_cb(/e,o,u->self.cb(e,o,u))
+        self.buf = bytes(-256)
+        BLE.conn_cb(cbp,self.buf)
+        BLE.set_MAC(bytes(bytes(MAC)),0)
+        self.connect()
+    end
+
+    def connect() # separated to call it from the berry console if needed
+        import BLE
+        BLE.set_svc("1812")
+        BLE.set_chr("2a4a") # the first characteristic we have to read
+        BLE.run(1) # read
+    end
+
+    def handle_read_CB(uuid) # uuid is the notifying characteristic
+        import BLE
+    # we just have to read these characteristics before we can finally subscribe
+        if uuid == 0x2a4a # ignore data
+            BLE.set_chr("2a4b")
+            BLE.run(1) # read next characteristic 
+        end
+        if uuid == 0x2a4b # ignore data
+            BLE.set_chr("2a4d")
+            BLE.run(1) # read next characteristic 
+        end
+        if uuid == 0x2a4d # ignore data
+            BLE.set_chr("2a4d")
+            BLE.run(3) # start notify
+        end
+    end
+
+    def handle_HID_notifiaction() # a very incomplete parser
+        if self.buf[14] == 1
+            print("ButtonA") # a MQTT message could actually trigger something
+        end
+        if self.buf[14] == 2
+            print("ButtonB")
+        end
+        if self.buf[14] == 8
+            print("ButtonX")
+        end
+        if self.buf[14] == 16
+            print("ButtonY")
+        end
+    end
+
+    def cb(error,op,uuid)
+        if error == 0
+            if op == 1
+                print(op,uuid)
+                self.handle_read_CB(uuid)
+            end
+            if op == 3
+            self.handle_HID_notification()
+            end
+            return
+        else
+            print(error)
+        end
+    end
+
+end
+
+xbox = XBOX("AABBCCDDEEFF")  # xbox controller MAC
+tasmota.add_driver(xbox)
+
\ No newline at end of file diff --git a/Bluetooth_old/index.html b/Bluetooth_old/index.html new file mode 100644 index 0000000000..ca7bb3cbae --- /dev/null +++ b/Bluetooth_old/index.html @@ -0,0 +1,25 @@ + Bluetooth old - Tasmota
Skip to content

Bluetooth old

The Bluetooth section of Tasmota currently consists of 2 driver classes, which, not least due to hardware restrictions, cannot be used together.
On the one hand there is support for the use of "iBeacons" on some modules of the HM-1x family and on ESP32 internal Bluetooth.
The second part consists of 3 drivers that can read the data from BLE sensors from the relatively diverse Xiaomi universe. These drivers offer very basic beacon functionality too.

Presence detection with iBeacons or BLE sensor gateway using HM-1x or nRF24L01(+) peripherals

iBeacon~

This feature is included only in tasmota-sensors.bin

Otherwise you must compile your build. Add the following to user_config_override.h:

#ifndef USE_IBEACON
+#define USE_IBEACON          // Add support for bluetooth LE passive scan of ibeacon devices 
+#endif
+

Tasmota uses a BLE 4.x module to scan for iBeacon devices. This driver is working with HM-10 and clones and HM16/HM17 Bluetooth modules and potentially with other HM-1x modules depending on firmware capabilities.

Using ESP32 built-in Bluetooth~

You must compile your build for the ESP32 (since v9.1). Change the following to user_config_override.h:

#ifdef ESP32
+  #define USE_IBEACON_ESP32    // Use internal ESP32 Bluetooth module
+#endif // ESP32
+

Features~

For a list of all available commands see Sensor52 command.

This driver reports all beacons found during a scan with its ID (derived from beacon's MAC address) prefixed with IBEACON_ and RSSI value.

Every beacon report is published as an MQTT tele/%topic%/SENSOR in a separate message:

tele/ibeacon/SENSOR = {"Time":"2020-03-24T20:09:40","IBEACON_FF34C21G2174":{"RSSI":-81}}
+tele/ibeacon/SENSOR = {"Time":"2020-03-24T20:09:42","IBEACON_DEAABC788BC1":{"RSSI":-60}}
+

If the beacon can no longer be found during a scan and the timeout interval has passed the beacon's RSSI is set to zero (0) and it is no longer displayed in the webUI

tele/ibeacon/SENSOR = {"Time":"2020-03-24T20:05:00","IBEACON_DEAABC788BC1":{"RSSI":-0}}
+

Tip

When first connected some modules will be in peripheral mode. You have to change it to central mode using commands Sensor52 1 and Sensor52 2.

Supported Devices~

All Apple compatible iBeacon devices should be discoverable.

Various nRF51822 beacons should be fully Apple compatible, programmable and their battery lasts about a year.

Cheap "iTag" beacons with a beeper. The battery on these lasts only about a month.

Tip

You can activate a beacon with a beeper using command IBEACON_%BEACONID%_RSSI 99 (ID is visible in webUI and SENSOR reports). This command can freeze the Bluetooth module and beacon scanning will stop. After a reboot of Tasmota the beacon will start beeping and scanning will resume.

Tasmota and BLE-sensors~

Different vendors offer Bluetooth solutions as part of the XIAOMI family often under the MIJIA-brand (while AQUARA is the typical name for a ZigBee sensor).
The sensors supported by Tasmota use BLE (Bluetooth Low Energy) to transmit the sensor data, but they differ in their accessibilities quite substantially.

Basically all of them use of so-called „MiBeacons“ which are BLE advertisement packets with a certain data structure, which are broadcasted by the devices automatically while the device is not in an active bluetooth connection.
The frequency of these messages is set by the vendor and ranges from one per 3 seconds to one per hour (for the battery status of the LYWSD03MMC). Motion sensors and BLE remote controls start to send when an event is triggered.
These packets already contain the sensor data and can be passively received by other devices and will be published regardless if a user decides to read out the sensors via connections or not. Thus the battery life of a BLE sensor is not influenced by reading these advertisements and the big advantage is the power efficiency as no active bi-directional connection has to be established. The other advantage is, that scanning for BLE advertisements can happen nearly parallel (= very quick one after the other), while a direct connection must be established for at least a few seconds and will then block both involved devices for that time.
This is therefore the preferred option, if technically possible (= for the supported sensors).

Most of the „older“ BLE-sensor-devices use unencrypted messages, which can be read by all kinds of BLE-devices or even a NRF24L01. With the arrival of "newer" sensors came the problem of encrypted data in MiBeacons, which can be decrypted in Tasmota (not yet with the HM-1x).
Meanwhile it is possible to get the needed "bind_key" with the help of an open-source project: https://atc1441.github.io/TelinkFlasher.html. At least the LYWSD03 allows the use of a simple BLE connection without any encrypted authentication and the reading of the sensor data using normal subscription methods to GATT-services (currently used on the HM-1x). This is more power hungry than the passive reading of BLE advertisements.
Other sensors like the MJYD2S are not usable without the "bind_key".

Supported Devices~

It can not be ruled out, that changes in the device firmware may break the functionality of this driver completely!

The naming conventions in the product range of bluetooth sensors in XIAOMI-universe can be a bit confusing. The exact same sensor can be advertised under slightly different names depending on the seller (Mijia, Xiaomi, Cleargrass, ...).

MJ_HT_V1 LYWSD02 CGG1 CGD1
temperature, humidity, battery temperature, humidity, battery temperature, humidity, battery temperature, humidity, battery
passive for all entities, reliable battery value battery only active, thus not on the NRF24L01, set clock and unit, very frequent data sending passive for all entities, reliable battery value battery only active, thus not on the NRF24L01, no reliable battery value, no clock functions
MiFlora LYWSD03MMC / ATC NLIGHT MJYD2S
temperature, illuminance, soil humidity, soil fertility, battery, firmware version temperature, humidity, battery motion motion, illuminance, battery, no-motion-time
passive only with newer firmware (>3.0?), battery only active, thus not on the NRF24L01 passive only with decryption or using custom ATC-firmware, no reliable battery value with stock firmware NRF24L01, ESP32 passive only with decryption, thus only NRF24L01, ESP32
YEE RC MHO-C401 MHO-C303
button press (single and long) temperature, humidity, battery temperature, humidity, battery
passive equal to the LYWS03MMC, but no custom firmware yet passive for all entities, set clock and unit, no alarm functions, very frequent data sending

passive: data is received via BLE advertisements active: data is received via bidrectional connection to the sensor

Devices with payload encryption~

The LYWSD03MMC, MHO-C401 and the MJYD2S will start to send advertisements with encrypted sensor data after pairing it with the official Xiaomi app. Out-of-the-box the sensors do only publish a static advertisement.
It is possible to do a pairing and get the necessary decryption key ("bind_key") here: https://atc1441.github.io/TelinkFlasher.html. This project also provides a custom firmware for the LYWSD03MMC, which then becomes an ATC and is supported by Tasmota too. Default ATC-setting will drain the battery more than stock firmware, because of very frequent data sending.
This key and the corresponding MAC of the sensor can be injected with the NRFKEY-command (or NRFMJYD2S). It is probably a good idea to save the whole config as RULE like that:

rule1 on System#Boot do backlog NRFkey 00112233445566778899AABBCCDDEEFF112233445566; NRFkey 00112233445566778899AABBCCDDEEFF112233445566; NRFPage 6; NRFUse 0; NRFUse 4 endon
+
(key for two sensors, 6 sensors per page in the WebUI, turn off all sensors, turn on LYWS03)

LYWSD03MMC sends encrypted sensor data every 10 minutes. As there are no confirmed reports about correct battery presentation of the sensor (always shows 99%), this function is currently not supported.
MJYD2S sends motion detection events and 2 discrete illuminance levels (1 lux or 100 lux for a dark or bright environment). Additionally battery level and contiguous time without motion in discrete growing steps (no motion time = NMT).

Working principle of Tasmota BLE drivers (>8.5.)~

The idea is to provide drivers with as many automatic functions as possible. Besides the hardware setup, there are zero or very few things to configure.
The sensor namings are based on the original sensor names and shortened if appropriate (Flower care -> Flora). A part of the MAC will be added to the name as a suffix.
All sensors are treated as if they are physically connected to the ESP8266 device. For motion and remote control sensors MQTT-messages will be published in (nearly) real time. The ESP32 and the HM-1x-modules are real BLE devices whereas the NRF24L01 (+) is only a generic 2.4 GHz transceiver with very limited capabilities.

Options to read out the LYWSD03MMC~
  1. Generate a bind_key
    The web-tool https://atc1441.github.io/TelinkFlasher.html allows the generation of a so-called bind_key by faking a pairing with the Xiaomi cloud. You can copy-paste this key and add the MAC to use this resultig key-MAC-string with key-command (NRFkey or MI32key). Then the driver will receive the sensors data roughly every 10 minutes (in two chunks for humidity and temperature with about a minute in between) and decode the data. This is the most energy efficient way. The current way of storing these keys on the ESP32 is to use RULES like that (for the NRF24L01 you would use NRFkey):

    rule1 on System#Boot do backlog MI32key 00112233445566778899AABBCCDDEEFF112233445566; MI32key 00112233445566778899AABBCCDDEEFFAABBCCDDEEFF endon
    +
    This option is currently not available for the HM-10 because of memory considerations as part of the standard sensor-firmware package.

  2. Flash custom ATC-firmware
    Use the same https://atc1441.github.io/TelinkFlasher.html to flash a custom ATC-firmware on the LYWSD03MMC. This will work out of the box with all three Tasmota-drivers. There is a slight chance of bricking the sensor, which would require some soldering and compiling skills to un-brick. This firmware does send data more frequently and is a little bit more power hungry than the stock firmware.

  3. Use active connections
    By default on the HM-10 (for legacy reasons) and at compile-time selectable on the ESP32 is the method to connect to the sensor from time to time. This circumvents the data encryption. This is very power hungry and drains the battery fast. Thus it is only recommended as fallback mechanism.

BLE Sensors using HM-1x~

This feature is included only in tasmota-sensors.bin

Otherwise you must compile your build. Add the following to user_config_override.h:

#ifndef USE_HM10
+#define USE_HM10          // Add support for HM-10 as a BLE-bridge (+9k3 code)
+#endif
+

Features~

Supported sensors will be connected to at a set interval (default interval equals TelePeriod). A subscription is established for 5 seconds and data (e.g. temperature, humidity and battery) is read and reported to an mqtt topic (Dew point is calculated):

tele/%topic%/SENSOR = {"Time":"2020-03-24T12:47:51","LYWSD03-52680f":{"Temperature":21.1,"Humidity":58.0,"DewPoint":12.5,"Battery":100},"LYWSD02-a2fd09":{"Temperature":21.4,"Humidity":57.0,"DewPoint":12.5,"Battery":2},"MJ_HT_V1-d8799d":{"Temperature":21.4,"Humidity":54.6,"DewPoint":11.9},"TempUnit":"C"}
+

After a completed discovery scan, the driver will report the number of found sensors. As Tasmota can not know how many sensors are meant to be discovered you have to force a re-scan until the desired number of devices is found.

Rule1 ON HM10#Found<6 DO Add1 1 ENDON ON Var1#State<=3 DO HM10Scan ENDON 
+
This will re-scan up to 3 times if less than 6 sensors are found.

Commands~

Command Parameters
HM10Scan Start a new device discovery scan
HM10Period Show interval in seconds between sensor read cycles. Set to TelePeriod value at boot.
HM10Baud Show ESP8266 serial interface baudrate (Not HM-10 baudrate)
<value> = set baudrate
HM10AT <command> = send AT commands to HM-10. See list
HM10Time <n> = set time time of a LYWSD02 only sensor to Tasmota UTC time and timezone. <n> is the sensor number in order of discovery starting with 0 (topmost sensor in the webUI list).
HM10Auto <value> = start an automatic discovery scan with an interval of <value> seconds to receive data in BLE advertisements periodically.
This is a passive scan and does not produce a scan response from the BLE sensor. It does not increase the sensors battery drain.
HM10Page Show the maximum number of sensors shown per page in the webUI list.
<value> = set number of sensors (default = 4)
HM10Beaconx Set a BLE device as a beacon using the (fixed) MAC-address
x - set beacon 1 .. 4
x= 0 - will start a BLE scan and print result to the console
<value> (12 or 17 characters) = use beacon given the MAC interpreted as a string AABBCCDDEEFF (also valid: aa:BB:cc:dd:EE:FF) MAC of 00:00:00:00:00:00 will stop beacon x

BLE Sensors using nRF24L01(+)~

Configuration~

You must compile your build. Change the following in my_user_config.h:

// -- SPI sensors ---------------------------------
+#define USE_SPI                                  // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC)
+#ifdef USE_SPI
+  #define USE_NRF24                              // Add SPI support for NRF24L01(+) (+2k6 code)
+  #ifdef USE_NRF24
+    #define USE_MIBLE                            // BLE-bridge for some Mijia-BLE-sensors (+4k7 code)
+

Sensors will be discriminated by using the Product-ID of the MiBeacon. A human readable short product name will be shown instead of the company-assigned ID of the BLE Public Device Address (= the "lower" 24 bits).

A TELE message could like look this:

10:13:38 RSL: stat/tasmota/STATUS8 = {"StatusSNS":{"Time":"2019-12-18T10:13:38","Flora-6ab577":{"Temperature":21.7,"Illuminance":21,"Humidity":0,"Fertility":0},"MJ_HT_V1-3108be":{"Temperature":22.3,"Humidity":56.1},"TempUnit":"C"}}
+

As the NRF24L01 can only read BLE-advertisements, only the data in these advertisements is accessible.
All sensors have an additional GATT-interface with more data in it, but it can not be read with a NRF24l01.

As we can not use a checksum to test data integrity of the packet, only data of sensors whose addresses showed up more than once (default = 3 times) will be published. Internally from time to time "fake" sensors will be created, when there was data corruption in the address bytes. These will be removed automatically.

Commands~

Command Parameters
NRFPage Show the maximum number of sensors shown per page in the webUI list.
<value> = set number of sensors (default = 4)
NRFIgnore 0 = all known sensor types active_(default)_
<value> = ignore certain sensor type (1 = Flora, 2 = MJ_HT_V1, 3 = LYWSD02, 4 = LYWSD03, 5 = CGG1, 6 = CGD1, 7 = NLIGHT,8 = MJYD2S,9 = YEERC (DEPRECATED, please switch to NRFUSE)
NRFUse 0 = all known sensor types inactive
<value> = ignore certain sensor type (1 = Flora, 2 = MJ_HT_V1, 3 = LYWSD02, 4 = LYWSD03, 5 = CGG1, 6 = CGD1, 7 = NLIGHT,8 = MJYD2S,9 = YEERC
NRFScan Scan for regular BLE-advertisements and show a list in the console
0 = start a new scan list
1 = append to the scan list
2 = stop running scan
NRFBeacon Set a BLE device as a beacon using the (fixed) MAC-address
<value> (1-3 digits) = use beacon from scan list
<value> (12 characters) = use beacon given the MAC interpreted as an uppercase string AABBCCDDEEFF
NRFKey Set a "bind_key" for a MAC-address to decrypt (LYWSD03MMC & MHO-C401). The argument is a 44 uppercase characters long string, which is the concatenation of the bind_key and the corresponding MAC.
<00112233445566778899AABBCCDDEEFF> (32 characters) = bind_key
<112233445566> (12 characters) = MAC of the sensor
<00112233445566778899AABBCCDDEEFF112233445566> (44 characters)= final string
NRFMjyd2s Set a "bind_key" for a MAC-address to decrypt sensor data of the MJYD2S. The argument is a 44 characters long string, which is the concatenation of the bind_key and the corresponding MAC.
<00112233445566778899AABBCCDDEEFF> (32 characters) = bind_key
<112233445566> (12 characters) = MAC of the sensor
<00112233445566778899AABBCCDDEEFF112233445566> (44 characters)= final string
NRFNlight Set the MAC of an NLIGHT
<value> (12 characters) = MAC interpreted as an uppercase string AABBCCDDEEFF

Beacon~

A simplified presence dection will scan for regular BLE advertisements of a given BT-device defined by its MAC-address. It is important to know, that many new devices (nearly every Apple-device) will change its MAC every few minutes to prevent tracking.
If the driver receives a packet from the "beacon" a counter will be (re-)started with an increment every second. This timer is published in the TELE-message, presented in the webUI and processed as a RULE. The stability of regular readings will be strongly influenced by the local environment (many BLE-devices nearby or general noise in the 2.4-GHz-band).

BLE Sensors on ESP32 using built-in Bluetooth~

Since Tasmota v9.1.0.1 this driver is part of the standard build 'tasmota32'. It must be enabled at runtime via setoption115 1.

To turn on/off support for decyrption, change the following in the driver code:

#define USE_MI_DECRYPTION
+
Without encryption support the driver will read data from found LYWSD03MMC via connection. This will increase power consumption of the sensor, but no "bind_key" is needed.

The driver will start to scan for known sensors automatically using a hybrid approach, when encryption support is deactivated. In the first place MiBeacons are passively received and only found LYWSD03MMC- and MHO-C401-sensors will be connected at the given period to read data in order to be as energy efficient as possible. Battery data is in general of questionable value for the LYWSD0x, CGD1, MHO-C401 and (maybe) Flora (some are even hard coded on the device to 99%). That's why only MJ_HT_V1, CGG1 (untested) and LYWSD03 (calculated battery level) will automatically update battery data. The other battery levels can be read by command. The internally very similar LYWS03MMC and MHO-C401 behave very confusing in delivering battery status. Both report (fixed) 99% or 100% via encrypted payload or connected reading of the battery characteristic. But they can deliver a correct battery voltage in their payload of the temperature and humidity data, which will be mapped to a percentage level by the driver.

Commands~

Command Parameters
MI32Period Show interval in seconds between sensor read cycles for the LYWSD03. Set to TelePeriod value at boot.
MI32Time <n> = set time time of a LYWSD02 only sensor to Tasmota UTC time and timezone. <n> is the sensor number in order of discovery starting with 0 (topmost sensor in the webUI list).
MI32Unit <n> = toggle the displayed temperature units of a LYWSD02 only sensor. <n> is the sensor number in order of discovery starting with 0 (topmost sensor in the webUI list). Reporting of the temperature is always in Celsius, this only changes the value shown on the device.
MI32Page Show the maximum number of sensors shown per page in the webUI list.
<value> = set number of sensors (default = 4)
MI32Battery Reads missing battery data for LYWSD02, Flora and CGD1.
MI32Key Set a "bind_key" for a MAC-address to decrypt sensor data (LYWSD03MMC, MJYD2S). The argument is a 44 uppercase characters long string, which is the concatenation of the bind_key and the corresponding MAC.
<00112233445566778899AABBCCDDEEFF> (32 characters) = bind_key
<112233445566> (12 characters) = MAC of the sensor
<00112233445566778899AABBCCDDEEFF112233445566> (44 characters)= final string
MI32Beaconx Set a BLE device as a beacon using the (fixed) MAC-address
x - set beacon 1 .. 4
x=0 - will start a BLE scan and send result via TELE-message
<value> (12 or 17 characters) = use beacon given the MAC interpreted as a string AABBCCDDEEFF (also valid: aa:BB:cc:dd:EE:FF) MAC of 00:00:00:00:00:00 will stop beacon x
MI32Blockx Ignore Xiaomi sensors using the (fixed) MAC-address
x=1 - show block list
x=0 - delete block list
x=1 + MAC-address - add MAC to to be blocked to the block list
x=0 + MAC-address - remove MAC to to be blocked to the block list
<value> (12 or 17 characters) = MAC interpreted as a string AABBCCDDEEFF (also valid: aa:BB:cc:dd:EE:FF)
MI32Option0 0 = sends only recently received sensor data
1 = aggregates all recent sensors data types
MI32Option1 0 = shows full sensor data at Teleperiod
1 = shows no sensor data at Teleperiod
MI32Option2 0 = sensor data only at Teleperiod (default)
1 = direct bridging of BLE data to MQTT messages

Tip

If you really want to read battery for LYWSD02, Flora and CGD1, consider doing it once a day with a RULE: RULE1 on Time#Minute=30 do MI32Battery endon This will update every day at 00:30 AM.

Beacon~

A count-up-timer starts for every beacon a with every received advertisement, starting with 0.

TELE-output:
"Beacon1":{"MAC":"11:22:33:44:55:66","CID":"0x0000","SVC":"0x0000","UUID":"0x0000","Time":4,"RSSI":0}}

RULE-example:
on Beacon2#time==30 do SOMETHING endon - is triggered 30 seconds after last packet was received
on system#boot do MI32Beacon2 AABBCCDDEEFF endon - save configuration for beacon 2

(following AD type, read here: https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/)
CID - company identifier
SVC - service data
UUID - service or class UUID

\ No newline at end of file diff --git a/Buttons-and-Switches/index.html b/Buttons-and-Switches/index.html new file mode 100644 index 0000000000..1953a47dfc --- /dev/null +++ b/Buttons-and-Switches/index.html @@ -0,0 +1,14 @@ + Buttons and Switches - Tasmota
Skip to content

Buttons and switches: why the difference and how to configure them

A typical device usually has at least one button (exception being bulbs and some lights) to control the power state(s). Additional buttons and switches can be wired to a free GPIO and configured in Module or Template settings.

Note

Other than relays/lights, Tasmota does not publish the state of components (switches, buttons, sensors, etc.) in real-time. State of components is transmitted automatically each TelePeriod via the SENSORS message.

Button vs. Switch~

A button (also called a push-button) is a momentary or non-latching switch which causes a temporary change in the state of an electrical circuit only while the switch is pressed. An automatic mechanism (i.e. a spring) returns the switch to its default position immediately afterwards, restoring the initial circuit condition.

A switch (more precisely a latching or toggle switch), when activated by the user, remains in that state until activated again.

Learn more about buttons and switches in this video.

Both have a similar function, but Tasmota distinguishes between a "Button" and a "Switch" in other ways.

Switch~

In Tasmota a Switch is any switch or push-button additionally connected to a free GPIO. Some possibilities include:

By default a switch toggles the corresponding power state (f.e. Switch1 controls Power1). Every time the switch gets flipped the power state of the relay toggles.

If you want to detach switches from relays read here.

Warning

If you define a switch with a number higher than available power outputs it will default to controlling Power1. Example: Switch4 on a device with Power1 and Power2 will control Power1.

And now, to make everything completely confusing: A push-button can be configured as a Switch and a toggle switch can be configured as a Button. Configuring a toggle switch as a Button is not recommended!

SwitchMode~

To change the behavior of a physical input peripheral configured as a Tasmota Switch<x> component, whether a toggle switch or a momentary switch (i.e., a push-button), use the SwitchMode command. If there is more than one Switch<x> component, use SwitchMode<x> where <x> is the number of your switch from the Tasmota GPIO configuration.

SwitchMode, as the name implies, applies ONLY to GPIO configured in Tasmota as a Switch<x> component. SwitchMode has NO impact on the behavior of GPIO configured as Button<x> components. SwitchMode sets the desired behavior of a Switch<x> component based on whether it's a switch or a push-button (i.e., a momentary switch) that is physically connected to the GPIO.

SwitchMode 0

Default mode

Set switch to toggle mode (0 = TOGGLE, 1 = TOGGLE).

Tasmota sends TOGGLE command each time the state of the circuit changes (closing or opening). In case of a push button attached Tasmota will send a TOGGLE command when pressed and another TOGGLE command when released.

Example

When the button is pressed, toggle the power to ring the doorbell; when the button is released, ring the doorbell again.

SwitchMode 1

Set switch to follow mode (0 = OFF, 1 = ON)

At the time when the circuit is closed, Tasmota will send ON and opening the circuit sends OFF.

Tip

You want to use SwitchMode 1 when connecting a toggle switch (e.g. a classic light switch) to your device. This way the "software switch" will mirror the state of the "hardware switch". If the real switch is in the "ON" position, the state in Tasmota is ON as well.

SwitchMode 2

Set switch to inverted follow mode (0 = ON, 1 = OFF)

At the time when the circuit is closed, Tasmota will send OFF and opening the circuit sends ON.

Tip

When connecting a momentary switch (i.e., a push-button) you will want to use SwitchMode 3..7.

SwitchMode 3

Set push-button mode (0 = TOGGLE, 1 = ON (default))

Tasmota will send a TOGGLE command when the button is pressed (closing the circuit). When the button is released (opening the circuit) nothing will happen. Default state is OFF and when pressed it's ON. (This trigger is known as rising-edge)

SwitchMode 4

Set inverted push-button mode (0 = OFF (default), 1 = TOGGLE)

Tasmota will send a TOGGLE command when the button is released (opening the circuit). When pressing the button (closing the circuit) nothing will happen. Default state is ON and when pressed it's OFF. (This trigger is known as falling-edge)

SwitchMode 5

Set push-button with long press mode (0 = TOGGLE, 1 = ON (default), long press = HOLD)

Tasmota will send a TOGGLE command when the button is released (opening the circuit). When pressing the button (closing the circuit) nothing will happen. Default state is ON and when pressed it's OFF. When held for the time set in SetOption32 (default = 4s), Tasmota sends HOLD (use Switch<x>#state=3 in rules).

SwitchMode 6

Set inverted push-button with long press mode (0 = OFF (default), 1 = TOGGLE, long press = HOLD)

Tasmota will send a TOGGLE command when the button pressed (closing the circuit). When the button is released (opening the circuit) nothing will happen. Default state is OFF and when pressed it's ON. When held for the time set in SetOption32 (default = 4s), Tasmota sends HOLD (use Switch<x>#state=3 in rules).

Long press or hold can be used in conjunction with rules to create additional features or to control another Tasmota device

SwitchMode 7
Set toggle push-button mode. Same as SwitchMode 0.
SwitchMode 8

Set switch to multi change toggle mode (0 = TOGGLE, 1 = TOGGLE, 2x change = HOLD).

Same as SwitchMode 0 but when the state of the circuit changes within 0.5s twice no TOGGLE commands are send but Tasmota sends HOLD (use Switch<x>#state=3 in rules).

Warning

When you change switch states fast (within 0.5s) some extra actions can be triggered using rules. ON and OFF power states are only changed when there is no second switch change within 0.5s.

SwitchMode 9

Set switch to multi change follow mode (0 = OFF, 1 = ON, 2x change = HOLD)

Same as SwitchMode 1 but when the state of the circuit changes within 0.5s twice no OFF/ON commands are send but Tasmota sends HOLD (use Switch<x>#state=3 in rules).

Warning

When you change switch states fast (within 0.5s) some extra actions can be triggered using rules. ON/OFFpower state is only changed when there is no second switch change within 0.5s.

SwitchMode 10

Set switch to multi change inverted follow mode (0 = ON, 1 = OFF, 2x change = HOLD)

Same as SwitchMode 2 but when the state of the circuit changes within 0.5s twice no ON/OFF commands are send but Tasmota sends HOLD (use Switch<x>#state=3 in rules).

Warning

When you change switch states fast (within 0.5s) some extra actions can be triggered using rules. ON/OFFpower state is only changed when there is no second switch change within 0.5s.

SwitchMode 11

Set switch to pushbutton with dimmer mode incl. double press feature

Note

Setoption32 must be smaller than 64, when you use switchmode 11 and 12 !!

Tasmota will send a TOGGLE command when the button is pressed for a short time and then is released (use Switch<x>#state=2 in rules).

When pressing the button (closing the circuit) for a long time (set in SetOption32), Tasmota will send repeated INC_DEC (increment or decrement the dimmer) commands for as long as the button is pressed (use Switch<x>#state=4 in rules).

Two different CLEAR commands are available. An immediate CLEAR command is send upon button release - no delay (use Switch<x>#state=7 in rules).

Releasing the button also starts an internal timer (time is set in SetOption32). When released for the time set in SetOption32, Tasmota will send a 'delayed' CLEAR command (use Switch<x>#state=6 in rules).

If the button is pressed again before the timeout, Tasmota will send an INV command. The INV command is for the controlling software (Home Assistant) to switch between incrementing and decrementing the dimmer (use Switch<x>#state=5 in rules).

If button is pressed twice (within time set in SetOption32), Tasmota will send a DOUBLE command. Note that this doesn't change behaviour of other switch states. So along with the DOUBLE command, TOGGLE command will also be fired twice upon a double press (use Switch<x>#state=8 in rules).

If the button is pressed only once and shorter than the time set in SetOption32, Tasmota will also send the POWER_DELAYED command (use Switch<x>#state=10 in rules) when no second press occurs within time set in SetOption32. You can use this for triggering single press events instead of using TOGGLE if you want to use single and double press individually (since TOGGLE is triggered for both single and double press). Keep in mind, that this event is delayed in comparison to TOGGLE.

Tip

Possible use case: using rules to create additional features or to control another Tasmota device.

SwitchMode 12

Set switch to inverted pushbutton with dimmer mode incl. double press feature. Same as Switchmode 11 but with inverted behaviour.

Note

Setoption32 must be smaller than 64, when you use switchmode 11 and 12 !!

SwitchMode 13

Set switch to "push to on" mode (1 = ON, 0 = nothing)

Tasmota will send an ON command when the button pressed (closing the circuit). When the button is released (opening the circuit) nothing will happen. Switch off using PulseTime.

SwitchMode 14

Set switch to inverted "push to on" mode (0 = ON, 1 = nothing)

This mode is useful with PIR sensors

SwitchMode 15

Send only MQTT message on switch change. This will stop the switch from controlling power outputs.

tele/tasmota/SENSOR = {"Time":"2021-01-01T00:00:00","Switch1":"OFF"}
+tele/tasmota/SENSOR = {"Time":"2021-01-01T00:00:00","Switch1":"ON"}
+
SwitchMode 16
Send only MQTT message on inverted switch change. This will stop the switch from controlling power outputs.
tele/tasmota/SENSOR = {"Time":"2021-01-01T00:00:00","Switch1":"ON"}
+tele/tasmota/SENSOR = {"Time":"2021-01-01T00:00:00","Switch1":"OFF"}
+

Also see SetOption114 below.

Button~

For Tasmota, a Button is typically a momentary push-button (or a capacitive touch button in some light switches). By default a button toggles the corresponding power state. Every time the button gets pressed a relay or light changes its Power state (ON or OFF). Besides toggling the Power state, a button is also used to activate multi press button functions, to do long press (HOLD) actions, or send messages to different MQTT topics.

Depending if you are using a push-to-make button or push-to-break button, as well as connecting the button between GPIO and GND or GPIO and VCC, different configurations are possible. The diagram beside (click to enlarge) present the various options:

To ignore default button behaviour of controlling power outputs you can:

  1. use SetOption73 1
  2. use ButtonTopic
  3. define a rule which triggers on Button<x>#State. Take note: If the rule trigger only certain states, default behaviour is suppressed only for those states.

Make Button1 publish its value to stat/custom-topic/BUTTON and not control Power1

Backlog ButtonTopic 0
+
Rule1 on Button1#state do Publish stat/custom-topic/BUTTON %value% endon
+Rule1 1
+

Multi-Press Functions~

Multipress functions for 2 and more presses cannot be changed using SetOptions or rules.

Danger

If you have changed ButtonTopic, SetOption1, SetOption11 or SetOption13 some of the listed functionality will be changed or removed.

Note

Button1 can directly control up to five relays. The number of the activated relay corresponds to the number of button presses and this feature is not present in the other buttons. When ButtonTopic is set to default 0 a button will always send its state for rules.

1 short press
Toggles the power state. This will blink the LED once and send an MQTT status message like stat/tasmota/POWER = ON or another one like stat/tasmota/BUTTON<x> = {"ACTION":"SINGLE"} when SetOption73 is enabled. The button state for rules is 2 (10 if Setoption73 is enabled).
2 short presses
When using Button1 toggles the second power state (if available on the device). This will blink the LED twice and send an MQTT status message like stat/tasmota/POWER2 = ON or another one like stat/tasmota/BUTTON<x> = {"ACTION":"DOUBLE"} when SetOption73 is enabled. The button state for rules is 11.
3 short presses
When using Button1 toggles the third power state (if available on the device). This will blink the LED three times and send an MQTT status message like stat/tasmota/POWER3 = ON or another one like stat/tasmota/BUTTON<x> = {"ACTION":"TRIPLE"} when SetOption73 is enabled. The button state for rules is 12.
4 short presses
When using Button1 toggles the fourth power state (if available on the device). This will blink the LED for times and send an MQTT status message like stat/tasmota/POWER4 = ON or another one like stat/tasmota/BUTTON<x> = {"ACTION":"QUAD"} when SetOption73 is enabled. The button state for rules is 13.
5 short presses
When using Button1 toggles the fifth power state (if available on the device). This will blink the LED five times and send an MQTT status message like stat/tasmota/POWER5 = ON or another one like stat/tasmota/BUTTON<x> = {"ACTION":"PENTA"} when SetOption73 is enabled. The button state for rules is 14.
6 short presses
Start WifiConfig 2. Can be disabled using SetOption1 1
**Long press**

There are two separate functions associated with a button long press based on how long it is held:

  1. When held continuously for 40 seconds (Configurable with SetOption32, value is 10x the configured hold time) Tasmota will reset to firmware defaults and restart.
  2. If enabled, button pressed for 4 seconds (Configurable with SetOption32) creates a HOLD action and send an MQTT status message like stat/tasmota/BUTTON<x> = {"ACTION":"HOLD"} when SetOption73 is enabled. The button state for rules is 3.

If ButtonRetain has been enabled the MQTT message will also contain the MQTT retain flag.

Danger

When a button is configured as inverted or with a Switchmode that keeps it as ON while depressed it activates the reset to firmware defaults function. Change the Button configuration or SwitchMode to avoid repeated reset to defaults or use Setoption1 1 to disable that function.

Warning

If you define a button with a number higher than available power outputs it will default to controlling Power1. Example: Button4 on a device with Power1 and Power2 will control Power1.

ButtonTopic~

ButtonTopic 0

Default option

By default a button controls the corresponding power state and doesn't send any MQTT messages itself.

No MQTT message will be published on account of the new button state. The message you see in console is the new power state that is controlled and not the button state.

ButtonTopic 1

Sets MQTT button topic to device %topic%.

When changing the state of the button an MQTT message is sent to the device topic with the payload according to SwitchMode set.

Example

Device topic tasmota with SwitchMode 3 yields the following message: MQT: cmnd/tasmota/POWER = ON

Notice the cmnd instead of the stat at the beginning.

This is the same as sending an MQTT command to this device, the device power state will be set to the defined state.

ButtonTopic <value>

Set button topic to a custom topic (32 characters max).

This will send an MQTT message to a custom defined topic similarly to option 1.

Example

For example, we set the topic to tasmota02 with ButtonTopic tasmota02. With SwitchMode 1 the device yields the following message: MQT: cmnd/tasmota02/POWER = TOGGLE

If you have another device with the topic tasmota02 this action will toggle its power state while not affecting anything on the tasmota device.

ButtonTopic Summary~

ButtonTopic 0 controls the power state directly. ButtonTopic 1 sends an MQTT message to the device topic. This sets the state of the devices power state accordingly. ButtonTopic <value> sends an MQTT message command to the custom topic. This does not change the state of the devices power state.

When a Button is set to a different topic than 0 is not possible to use Button<x>#State as a trigger for rules.

Changing Default Functionality~

If a ButtonTopic (and if SetOption1 1) or SwitchTopic 1 is defined (and SwitchMode is set to 5 or 6) and a button is pressed longer than defined Key Hold Time (SetOption32 default 4 seconds) an MQTT message like cmnd/%topic%/POWER HOLD will be sent. HOLD can be changed with StateText4.

Command SetOption11 allows for swapping the functionality between the SINGLE and DOUBLE press of the push button.

These changes result in the following:

Action matrix

Example~

You can control a ceiling fan from a Sonoff Touch: If your standard topic of Sonoff Touch is light and the ceiling fan topic is ceilingfan issue these commands on the Sonoff Touch to activate the double press feature.

ButtonTopic ceilingfan
+SetOption11 1
+
All of the above is easier accomplished using Rules!

AC Frequency Detection Switch~

Some devices, such as BlitzWolf BW-SS5 or Moes MS-104B, use mains frequency detection on their switch inputs. Whenever the connected switch or button is pressed there are 50/60 Hz pulses on the switch input. Inside the switch there's a frequency detection circuit which is connected to a GPIO of the ESP8266 chip which counts those pulses. Prior to Tasmota 8.4 this kind of switching was handled using Counter sensors and scripting which is now simplified.

You can imagine this algorithm as a leaking bucket. Every pulse adds water to the bucket (little more than leaking out in a cycle), but the water is dripping countinously. If the bucket is full, we will treat the switch on. If there's no pulses, the bucket will be empty, and the we will turn off the switch. The size of the bucket is the debouncing time which controls the sensitivity of the algorithm. If the mains frequency is 50 Hz, a whole AC wave is 20 msec long (for 60 Hz it's about 17 msec; 1000 / frequency if we want the result in milliseconds). The exact frequency is not really important, because we add more water for every pulse.

After you have assigned a Switch<x> to the GPIO connected to the AC frequency detection circuit use the 'SwitchDebounce' command to set the number of pulses required for the switch to be recognized as on or off. For example: SwitchDebounce 69 will turn the switch on after three pulses and turn it off after three missing ones (3 * 20 msec is 60 and the last digit must be 9 to activate the AC detection). You will probably have to experiment with the values depending on your AC frequency and the devices frequency detection implementation.

Once the feature is enabled you can use this switch as any regular switch!

Detach Switches with...~

SetOption114~

When SetOption114 1 all switches are detached from their respective relays and will send MQTT messages instead in the form of {"Switch<x>":{"Action":"<state>"}}.

Example

When switch one is toggle to "ON":{"Switch1":{"Action":"ON"}}

SwitchMode 15~

With command SwitchMode<x> 15 you can decouple Switch<x> from its power output and it will instead send MQTT messages in the following format:

MQT: tele/tasmota/SENSOR = {"Time":"2021-01-01T00:00:00","Switch1":"OFF"}
+MQT: tele/tasmota/SENSOR = {"Time":"2021-01-01T00:00:01","Switch1":"ON"}
+
You can globally change the status text with StateText command.

You can change the default "Switch1" text using SwitchText<x> command. For example: SwitchText1 Garage_Door_State

When Setoption114 1 is used together with SwitchMode<x> 15, Tasmota Integration in Home Assistant will create a binary sensor in HA using the Default text "String1" or the text mentioned in the SwitchText command.

Rules~

Use rules to send messages to different MQTT topics or send commands to other Tasmota devices when switch state (defined by SwitchMode) changes.

To ignore the default behaviour define a rule which triggers on Switch<x> for all state changes or on Switch<x>#State for specific state changes. If a rule matches only certain states, default switch behaviour is suppressed only for those states.

Example

Make Switch1 publish any value change to stat/custom-topic/SWITCH1 and not control Power1

Backlog SwitchMode 1; SwitchTopic 0
+
Backlog Rule1 on Switch1#state do Publish stat/custom-topic/SWITCH1 %value% endon; Rule1 1
+

SwitchTopic~

Warning

When using SwitchTopic 1 or 2 (or ButtonTopic 1 or 2) and your MQTT broker becomes unavailable, Tasmota falls back to default SwitchTopic 0 (or ButtonTopic 0), which is not optimal.
To avoid this, we recommend using first two options instead.

If you still need to use SwitchTopic read on!

SwitchTopic 0

Default mode

By default a switch controls the corresponding power state and doesn't send any MQTT messages itself.

No MQTT message will be published on account of the new switch state. The message you see in console is the new power state that is controlled and not the switch state.

SwitchTopic 1

Sets MQTT switch topic to device %topic%

When changing the state of the switch an MQTT message is sent to the device topic with the payload according to SwitchMode set.

Example

Device topic tasmota with SwitchMode 3 yields the following message: MQT: cmnd/tasmota/POWER = TOGGLE

Notice the cmnd instead of the stat at the beginning.

This is the same as sending an MQTT commands to this device, the device power state will be set to the defined state.

SwitchTopic <value>

Set switch topic to a custom topic (32 characters max)

This will send an MQTT message to a custom defined topic similarly to option 1.

In the following example, we set the topic to tasmota02 with SwitchTopic tasmota02.

Example

Device topic tasmota with SwitchMode 1 and custom topic tasmota02 yields the following message: MQT: cmnd/tasmota02/POWER = ON

If you have another device with the topic tasmota02 this action will turn on its power while not affecting anything on the tasmota device.

SwitchTopic Summary~

SwitchTopic 0 controls the power state directly. SwitchTopic 1 sends an MQTT message to the device topic. This sets the state of the devices power accordingly. SwitchTopic <value> sends an MQTT message command to the custom topic. This does not change the state of the devices power.


For a practical application of everything mentioned in this article read about this excellent LEGO nightstand switch project.

\ No newline at end of file diff --git a/Buttons-schematics/index.html b/Buttons-schematics/index.html new file mode 100644 index 0000000000..894fee9aba --- /dev/null +++ b/Buttons-schematics/index.html @@ -0,0 +1 @@ + Buttons schematics - Tasmota
\ No newline at end of file diff --git a/Buzzer/index.html b/Buzzer/index.html new file mode 100644 index 0000000000..9d2fccb1bb --- /dev/null +++ b/Buzzer/index.html @@ -0,0 +1 @@ + Buzzer - Tasmota
Skip to content

Buzzer

Tasmota gives you the option to control the sound pattern of a buzzer.

Note

A device LED can be assigned as a Buzzer component to display a blink pattern.

Buzzer command~

Parameters for the Buzzer command can be

<count>,<beep>,<silence>,<tune>
all parameters are optional. (default is 1,1,1 (one 100 millisecond beep)).

<count>
number of beeps -1 for infinite, -2 to follow state of LED1;» v8.1.0.6

<beep>
duration of one beep in 100 millisecond steps

<silence>
duration of silence between beeps 100 millisecond steps

<tune>
is a 32-bit bitmask where a 1 bit beeps and a 0 bit is silence according to <beep> and <silence>, respectively. The tune is played from most significant bit (MSB) to least significant bit (LSB). Leading and trailing 0 bits are ignored. If <tune> is specified, <count> is ignored (<count> for <tune> supported in » v8.1.0.6). If <tune> is 0, it is ignored.

Examples: 3 - Beep three times with 100 milliseconds duration and 100 milliseconds pause
2,3 - Beep twice with 300 milliseconds duration and 100 milliseconds pause
2,3,4 - Beep twice with 300 milliseconds duration and 400 milliseconds pause
1,2,3,0xF54 (0000 0000 0000 0000 0000 1111 0101 0100). Each 1 bit beeps for 200 milliseconds and each bounded 0 bit pauses for 300 milliseconds

0 = stop active buzzer cycle  » v6.6.0.18

\ No newline at end of file diff --git a/CC2530/index.html b/CC2530/index.html new file mode 100644 index 0000000000..c3023b0f7a --- /dev/null +++ b/CC2530/index.html @@ -0,0 +1,37 @@ + CC253x Zigbee module - Tasmota
Skip to content

CC253x Zigbee module~

CC2530, CC2531, and CC2538 are Texas Instruments system-on-chips (SoCs) for Zigbee communication

Any Texas Instruments CC253x series based module can serve as a coordinator if it has Z-Stack firmware flashed. See list of supported modules with their pinouts and flashing instructions since they are different for each device.

Info

Z-Stack Home 1.2 is fully supported in Tasmota allow the use of CC2530, CC2531, and CC2538 Zigbee MCU chips/SoCs/modules. There is also preliminary support for Z-Stack 3.x as of Tasmota v9.2.0.4 which allow the use of more powerful Zigbee MCU chips/SoCs/modules from Texas Instruments CC26x2 and CC13x2 series (example CC2652P, CC2652R, CC2652RB, CC1352P and CC1352R).

Info

You cannot use a Zigbee adapter in USB mode! Flash it with Z-Stack firmware and it will work in serial mode. You will have to wire the Zigbee MCU chips/SoC/modules for serial communication using TX and RX pins or solder-pads. USB port cannot be used for communicating with a Zigbee MCU chip/module.

Wi-Fi Adapter~

Using an ESP82xx device such as a Wemos D1 Mini or a NodeMCU to flash the CC2530 is a lower cost alternative than using a single purpose CC_DEBUGGER.

In normal operation two free GPIOs are needed for the serial communication with the CC2530.

Custom PCBs~

These PCBs make all the connections required to flash the CC2530 and to run Z2T:

SuperHouse.tv~

Jon Oxer created a custom PCB to connect a Wemos D1 Mini and a CC2530 board (with or without CC2591).

Complete module

Charles (aka hallard)~

Mainly based on Jon Oxer PCB, Charles created a new one with multi connect option for CC2538 + CC2592 to connect a Wemos D1 Mini/Raspberry PI or even with USB a CC2538 with CC2592 module. This works with awesome support of ZStack V3 by @s-hadinger

Complete module
Fully assembled with WeMos Mini D1 Fully assembled, standalone mode With enclosure

Check the dedicated github repo for detailed information

H4NC~

User h4nc created a custom PCB to connect a NodeMCU and a CC2530 board.

You can also get a complete Z2T module with case, pre-flashed and ready to configure and deploy.

Configuration~

Flash Zigbee Adapter~

Zigbee2Tasmota requires a TI CC2530 based module flashed with Z-Stack CC2530 firmware file from Koen Kanters.

Due to memory constraints of the CC2530, you can only pair 16 devices to a coordinator (See details).

Note

There is an alternative firmware allowing for Zigbee routers to create a mesh network and go beyond 16 devices. This is currently not tested nor supported by Zigbee2Tasmota. It may be added later.

Flashing options:

  • Flashing with CCLoader and ESP8266 (recommended)
  • Flashing with CCLib and ESP8266
  • Flash with a dedicated CC Debugger and PC

Flash Tasmota~

Once the flashing process completes, you can re-use the ESP82xx and flash Tasmota with Zigbee2Tasmota enabled tasmota-zigbee.bin binary. Otherwise, you can use any ESP82xx device.

Optional~

Run the ESP at 160MHz instead of 80MHz which ensures higher reliability in serial communication with CC2530.

In platformio_override.ini uncomment line 51:

board_build.f_cpu = 160000000L

If you find that your Zigbee2Tasmota operation is unstable, you may have an ESP82xx device that cannot operate reliably at the higher frequency. If you are using hardware serial (see below) and you still have unreliability, try compiling for 80MHz (reverse the options above) and flash the ESP82xx device again to see if operating at a lower frequency improves stability. Running at 80MHz will impact software serial communications so hardware serial is highly recommended if running the ESP82xx at 80MHz.

Flash the newly compiled binary using the normal flashing process.

Connect CC2530 to Tasmota~

If you are using your ESP82xx device to flash the Zigbee adapter as described in tutorials you may want to leave these connections in place in case you ever need to update Zigbee firmware. If not, any of the free GPIOs can be used.

It is recommended that hardware serial pins be used (GPIO1/GPIO3 or GPIO13[Rx]/GPIO15[Tx])

Due to ESP82xx GPIO pin constraints, GPIO15 can only be used as serial Tx.

The interface between the ESP82xx Wi-Fi device and the Zigbee module uses high speed serial.

Tip

Tasmota also provides serial communications emulation through software (i.e., software serial). This allows any GPIO to be used. TasmotaSerial version 2.4.x (PR #6377) has improved the reliability of software serial making it feasible for use in this application. However, if you have an option to use hardware serial, choose that.

Z2T uses software serial by default to allow for serial logging on GPIO1/GPIO3

Use SerialLog 0 to enable hardware serial on GPIO13[Rx]/GPIO15[Tx].

Recommended wiring:

ESP
Device
Tasmota
Zigbee Module
GPIO13 Zigbee RX (166) CC_TXD
(A.K.A. P0_3)
GPIO15 Zigbee TX (165) CC_RXD
(A.K.A. P0_2)
3V3 VCC
GND GND

Tasmota Settings~

In the Configuration -> Configure Module page assign:

  • GPIO13 to Zigbee RX
  • GPIO15 to Zigbee TX

You can quickly configure Tasmota using a custom template instead.

Use this one for the recommended wiring scheme:

{"NAME":"Zigbee","GPIO":[0,0,0,0,0,0,0,0,0,166,0,165,0],"FLAG":0,"BASE":18}
+

First Run~

When the Tasmota device boots, Zigbee2Tasmota will wait for 15 seconds before initializing the CC2530. This time allows for Wi-Fi and MQTT to connect (hopefully).

First boot:

MQT: tele/%topic%/RESULT = {"ZbState":{"Status":1,"Message":"CC2530 booted","RestartReason":"Watchdog","MajorRel":2,"MinorRel":6}}
+MQT: tele/%topic%/RESULT = {"ZbState":{"Status":50,"MajorRel":2,"MinorRel":6,"MaintRel":3,"Revision":20190608}}
+MQT: tele/%topic%/RESULT = {"ZbState":{"Status":2,"Message":"Resetting configuration"}}
+MQT: tele/%topic%/RESULT = {"ZbState":{"Status":3,"Message":"Configured, starting coordinator"}}
+MQT: tele/%topic%/RESULT = {"ZbState":{"Status":51,"IEEEAddr":"0x00124B00199DF06F","ShortAddr":"0x0000","DeviceType":7,"DeviceState":9,"NumAssocDevices":0}}
+MQT: tele/tasmota/Zigbee_home/RESULT = {"ZbState":{"Status":0,"Message":"Started"}}
+ZIG: Zigbee started
+ZIG: No zigbee devices data in Flash
+

Zigbee will automatically boot the CC2530 device, configure the device and wait for Zigbee messages.

Normal boot looks like:

MQT: tele/%topic%/RESULT = {"ZbState":{"Status":1,"Message":"CC2530 booted","RestartReason":"Watchdog","MajorRel":2,"MinorRel":6}}
+MQT: tele/%topic%/RESULT = {"ZbState":{"Status":50,"MajorRel":2,"MinorRel":6,"MaintRel":3,"Revision":20190608}}
+MQT: tele/%topic%/RESULT = {"ZbState":{"Status":3,"Message":"Configured, starting coordinator"}}
+MQT: tele/%topic%/RESULT = {"ZbState":{"Status":51,"IEEEAddr":"0x00124B00199DF06F","ShortAddr":"0x0000","DeviceType":7,"DeviceState":9,"NumAssocDevices":0}}
+MQT: tele/%topic%/RESULT = {"ZbState":{"Status":0,"Message":"Started"}}
+ZIG: Zigbee started
+ZIG: Zigbee devices data in Flash (516 bytes)
+

You can force a factory reset of your CC2530 with ZigbeeReset 1 and reboot

\ No newline at end of file diff --git a/CCLoader/index.html b/CCLoader/index.html new file mode 100644 index 0000000000..8bf8aa45e1 --- /dev/null +++ b/CCLoader/index.html @@ -0,0 +1,4 @@ + CCLoader - Tasmota
Skip to content

CCLoader~

development version feature

This is a port of the CCLoader utility from RedBearLab in form of a driver for Tasmota. The main difference is, that now only one ESP8266 is needed to do all the tasks. As a result OTA-upgrades are possible too.

The driver is tested for a CC2531, but should work on every CC25xx-device, that is supported by the original CCLoader.

The wiring to the debug pins (= programming pins) of the CC25xx boards stays the same. It is possible to leave all wires connected (VCC,GND,RX,TX).

In the current version of the Tasmota driver the pins are hardcoded and could be changed easily in the code:

int CCL_RESET = 14; //GPIO14=D5 on NodeMCU/WeMos D1 Mini  
+int CCL_DD = 4; //GPIO4=D2 on NodeMCU/WeMos D1 Mini  
+int CCL_DC = 5; //GPIO5=D1 on NodeMCU/WeMos D1 Mini  
+

The driver must be enabled manually for a self compiled Tasmota binary.
#define USE_CCLOADER

After reboot the connected CC25xx chip will be reported in the console and the WebUI.

CCLoader_1

Then chose Firmware Upgrade in the WebUI.

CCLoader_2

Now download the correct firmware as a .bin-file, if you haven't done already. Do not use a .hex-file. These can be found for various chips here:
https://github.com/arendst/Tasmota/tree/development/tools

Select Upgrade by file upload, like you would do for a OTA upgrade of the Tasmota firmware. If a CC25xx chip was successfully detected and the selected file passes a (very minimalistic) check, this will trigger the upload to the CC25xx.

CCLoader_3

This will start the upgrade, which will take several seconds. The progress will be printed to the console and after the finish, the device gets rebooted. A power cycle is recommended. That’s it.

Now it might be a good time to upload a Zigbee- or HM1x-firmware to your ESP8266.

\ No newline at end of file diff --git a/Codes-for-IR-Remotes/index.html b/Codes-for-IR-Remotes/index.html new file mode 100644 index 0000000000..db4862abcb --- /dev/null +++ b/Codes-for-IR-Remotes/index.html @@ -0,0 +1,22 @@ + Codes for IR Remotes - Tasmota
Skip to content

Codes for IR Remotes

The IR Codes can be used with any devicewith an IR sender.

The codes will also fit other devices from same manufacturer or series. Please try out...

Feel free to contribute this list

TV's~

IR Codes TV Panasonic TX65FXW784~

Example received Code: tele/sonoffIRBridge_1/RESULT = {"IrReceived":{"Protocol":"PANASONIC","Bits":48,"Data":"0x100BCBD"}}

Example IRsend Command: IRsend {"Protocol":"PANASONIC","Bits":48,"Data":0x100BCBD}

  • 0x40040100BCBD="ON/OFF"
  • 0x400401004C4D="Mute"
  • 0x400401000405="Vol+"
  • 0x400401008485="Vol-"
  • 0x400401002C2D="P+"
  • 0x40040100ACAD="P-"
  • 0x400401007273="Left"
  • 0x40040100F2F3="Right"
  • 0x400401005253="Up"
  • 0x40040100D2D3="Down"
  • 0x400401009293="OK"
  • 0x400401004A4B="Menu"
  • 0x400401000E0F="Red"
  • 0x400401004E4F="Yellow"
  • 0x400401008E8F="Green"
  • 0x40040100CECF="Blue"
  • 0x40040100ECED="Last View"
  • 0x400401206D4C="My App"
  • 0x400401904FDE="Netflix"
  • 0x40040190F160="Apps"
  • 0x400401900392="Play"
  • 0x400401908312="Pause"
  • 0x4004019043D2="Stop"
  • 0x40040190C352="Forward"
  • 0x4004019023B2="Back"
  • 0x400401000809="Key 1"
  • 0x400401008889="Key 2"
  • 0x400401004849="Key 3"
  • 0x40040100C8C9="Key 4"
  • 0x400401002829="Key 5"
  • 0x40040100A8A9="Key 6"
  • 0x400401006869="Key 7"
  • 0x40040100E8E9="Key 8"
  • 0x400401001819="Key 9"
  • 0x400401009899="Key 0"
  • 0x40040190BB2A="Title forward"
  • 0x400401903BAA="Title back"
  • 0x40040190A332="Record"
  • 0x40040190E170="Guide"
  • 0x40040100CBCA="Exit"
  • 0x400401002B2A="Back"
  • 0x40040190E574="Option"
  • 0x400401009C9D="Info"
  • 0x400401400C4D="TV"
  • 0x40040100A0A1="AV"
  • 0x40040180C041="Text"
  • 0x40040180A021="STTL"
  • 0x400401000A0B="Picture"
  • 0x400401003534="Help"

IR Remote Codes Sony KDL-EX540~

Common buttons should work across multiple models

Input {"Protocol":"SONY","Bits":12,"Data":"0xA50"}

Power On {"Protocol":"SONY","Bits":12,"Data":"0x750"}

Power Off {"Protocol":"SONY","Bits":12,"Data":"0xF50"}

Power Toggle {"Protocol":"SONY","Bits":12,"Data":"0xA90"}

Vol + {"Protocol":"SONY","Bits":12,"Data":"0x490"}

Vol - {"Protocol":"SONY","Bits":12,"Data":"0xC90"}

Mute {"Protocol":"SONY","Bits":12,"Data":"0x290"}

Ch + {"Protocol":"SONY","Bits":12,"Data":"0x090"}

Ch - {"Protocol":"SONY","Bits":12,"Data":"0x890"}

Theatre Mode {"Protocol":"SONY","Bits":15,"Data":"0x03EE"}

Play/Pause {"Protocol":"SONY","Bits":15,"Data":"0x2CE9"}

Stop {"Protocol":"SONY","Bits":15,"Data":"0x0CE9"}

Rew {"Protocol":"SONY","Bits":15,"Data":"0x6CE9"}

Fwd {"Protocol":"SONY","Bits":15,"Data":"0x1CE9"}

Sync Menu {"Protocol":"SONY","Bits":15,"Data":"0x0D58"}

Prev {"Protocol":"SONY","Bits":15,"Data":"0x1EE9"}

Next {"Protocol":"SONY","Bits":15,"Data":"0x5EE9"}

i-Manual {"Protocol":"SONY","Bits":15,"Data":"0x6F58"}

Scene {"Protocol":"SONY","Bits":15,"Data":"0x0F58"}

Aspect {"Protocol":"SONY","Bits":15,"Data":"0x5E25"}

Digital/Analog {"Protocol":"SONY","Bits":15,"Data":"0x58EE"}

PIP {"Protocol":"SONY","Bits":15,"Data":"0x7725"}

Internet Video {"Protocol":"SONY","Bits":15,"Data":"0x4F58"}

Favourite {"Protocol":"SONY","Bits":15,"Data":"0x37EE"}

Guide {"Protocol":"SONY","Bits":15,"Data":"0x6D25"}

Info {"Protocol":"SONY","Bits":12,"Data":"0x5D0"}

Return {"Protocol":"SONY","Bits":15,"Data":"0x62E9"}

Options {"Protocol":"SONY","Bits":15,"Data":"0x36E9"}

Home {"Protocol":"SONY","Bits":12,"Data":"0x070"}

Up {"Protocol":"SONY","Bits":12,"Data":"0x2F0"}

Down {"Protocol":"SONY","Bits":12,"Data":"0xAF0"}

Left {"Protocol":"SONY","Bits":12,"Data":"0x2D0"}

Right {"Protocol":"SONY","Bits":12,"Data":"0xCD0"}

Enter {"Protocol":"SONY","Bits":12,"Data":"0xA70"}

Red {"Protocol":"SONY","Bits":15,"Data":"0x52E9"}

Green {"Protocol":"SONY","Bits":15,"Data":"0x32E9"}

Yellow {"Protocol":"SONY","Bits":15,"Data":"0x72E9"}

Blue {"Protocol":"SONY","Bits":15,"Data":"0x12E9"}

1 {"Protocol":"SONY","Bits":12,"Data":"0x010"}

2 {"Protocol":"SONY","Bits":12,"Data":"0x810"}

3 {"Protocol":"SONY","Bits":12,"Data":"0x410"}

4 {"Protocol":"SONY","Bits":12,"Data":"0xC10"}

5 {"Protocol":"SONY","Bits":12,"Data":"0x210"}

6 {"Protocol":"SONY","Bits":12,"Data":"0xA10"}

7 {"Protocol":"SONY","Bits":12,"Data":"0x610"}

8 {"Protocol":"SONY","Bits":12,"Data":"0xE10"}

9 {"Protocol":"SONY","Bits":12,"Data":"0x110"}

Text {"Protocol":"SONY","Bits":12,"Data":"0xFD0"}

0 {"Protocol":"SONY","Bits":12,"Data":"0x910"}

Subtitles {"Protocol":"SONY","Bits":15,"Data":"0x0AE9"}

Audio Track {"Protocol":"SONY","Bits":12,"Data":"0xE90"}

HDMI 1 {"Protocol":"SONY","Bits":15,"Data":"0x2D58"}

HDMI 2 {"Protocol":"SONY","Bits":15,"Data":"0x6D58"}

HDMI 3 {"Protocol":"SONY","Bits":15,"Data":"0x1D58"}

HDMI 4 {"Protocol":"SONY","Bits":15,"Data":"0x5D58"}

Video 1 {"Protocol":"SONY","Bits":12,"Data":"0x030"}

Video 2 {"Protocol":"SONY","Bits":12,"Data":"0x830"}

Video 3/Component 1 {"Protocol":"SONY","Bits":12,"Data":"0x430"}

PC {"Protocol":"SONY","Bits":12,"Data":"0xC30"}

Digital TV {"Protocol":"SONY","Bits":15,"Data":"0x25EE"}

IR Codes TV LG 55UH8509~

Example received Code: tele/sonoffIRBridge_1/RESULT = {"IrReceived":{"Protocol":"NEC","Bits":32,"Data":"0x20DF10EF"}}

Example IRsend Command:

IRsend {"Protocol":"NEC","Bits":32,"Data":0x20DF10EF}

or with mosquitto_pub:

mosquitto_pub -q 2 -t cmnd/sonoffIRBridge_1/IRSend -m '{"protocol": "NEC","bits": 32, "data": 0x20DF10EF}'

  • 0x20DF10EF="ON/OFF"
  • 0x20DF0FF0="TV"
  • 0x20DF9E61="Ratio"
  • 0x20DFD02F="Input"
  • 0x20DFA956="Energy"
  • 0x20DF8877="Key 1"
  • 0x20DF48B7="Key 2"
  • 0x20DFC837="Key 3"
  • 0x20DF28D7="Key 4"
  • 0x20DFA857="Key 5"
  • 0x20DF6897="Key 6"
  • 0x20DFE817="Key 7"
  • 0x20DF18E7="Key 8"
  • 0x20DF9867="Key 9"
  • 0x20DFCA35="List"
  • 0x20DF08F7="Key 0"
  • 0x20DF58A7="Q.View"
  • 0x20DF40BF="Vol+"
  • 0x20DFC03F="Vol-"
  • 0x20DFCE31="Vol-"
  • 0x20DF7887="Fav"
  • 0x20DF3BC4="3D"
  • 0x20DF906F="Mute"
  • 0x20DF00FF="P+"
  • 0x20DF807F="P-"
  • 0x20DFC23D="Settings"
  • 0x20DF55AA="Info"
  • 0x20DFA25D="Q.Menu"
  • 0x20DFE01F="Left"
  • 0x20DF609F="Right"
  • 0x20DF02FD="Up"
  • 0x20DF827D="Down"
  • 0x20DF22DD="OK"
  • 0x20DF14EB="Back"
  • 0x20DFD52A="Guide"
  • 0x20DFDA25="Exit"
  • 0x20DF4EB1="Red"
  • 0x20DF8E71="Green"
  • 0x20DFC639="Yellow"
  • 0x20DF8679="Blue"
  • 0x20DF04FB="Text"
  • 0x20DF847B="T.Opt"
  • 0x20DF9C63="Subtitle"
  • 0x20DF8D72="Stop"
  • 0x20DF0DF2="Play"
  • 0x20DF5DA2="Pause"
  • 0x20DFF10E="Backward"
  • 0x20DF718E="Forward"
  • 0x20DF7E81="Simplink"
  • 0x20DF8976="AD"
  • 0x20DF0CF3="AV Mode"
  • 0x20DF3EC1="Live TV"
  • 0x20DF42BD="Live TV"
  • 0x20DF35CA="Status"
  • 0x20DF50AF="Audio Language"
  • 0x20DF57A8="Shortmenu"
  • 0x20DF5AA5="AV"
  • 0x20DF5EA1="online manual"
  • 0x20DF619E="Volume to 50"
  • 0x20DF21DE="Volume to 80"
  • 0x20DFE11E="Volume to 100"
  • 0x20DF6B94="Radio/TV"
  • 0x20DF708F="doze function"
  • 0x20DFA35C="Display off"
  • 0x20DFD728="Standby"
  • 0x20DF23DC="Power on"
  • 0x20DFB44B="Settings of remote"
  • 0x20DF956A="Program guide"
  • 0x20DFF00F="Radio/TV"
  • 0x20DF738C="HDMI 1"
  • 0x20DF33CC="HDMI 2"
  • 0x20DF9768="HDMI 3"
  • 0x20DFFD02="Component"
  • 0x20DFF50A="Live Zoom"
  • 0x20DFF20D="Input info"
  • 0x20DFB24D="Picture mode"
  • 0x20DF09F6="Recordings"
  • 0x20DF4AB5="Audio mode"
  • 0x20DF7B84="Register magic remote"
  • 0x20DFAE51="directly register magic remote"
  • 0x20DF1CE3="Subtitles"
  • 0x20DFCC33="Presentation in shop"
  • 0x20DFBD42="Start recording"
  • 0x20DFAD52="LG TV Plus"
  • 0x20DFDD22="Sports mode"
  • 0x20DF1EE1="Search"

  • 0x20DFDF20="Warning: instart"

  • 0x20DF7F80="Warning: Poweronly"
  • 0x20DFFF00="Warning: Easy adjust"
  • 0x20DF5FA0="Warning: instop"
  • 0x20DF5FA0="Enter Password"
  • 0x20DF3FC0="Warning: Reset to factory"

Generic VEON TV (eg model SRO322016)~


button code
On/Off {"Protocol":"NEC","Bits":32,"Data":"0x00FEA857"}
Mute {"Protocol":"NEC","Bits":32,"Data":"0x00FE6897"}
Sleep {"Protocol":"NEC","Bits":32,"Data":"0x00FE38C7"}
Source {"Protocol":"NEC","Bits":32,"Data":"0x00FE48B7"}
Info {"Protocol":"NEC","Bits":32,"Data":"0x00FE28D7"}
EPG {"Protocol":"NEC","Bits":32,"Data":"0x00FEAA55"}
Vol+ {"Protocol":"NEC","Bits":32,"Data":"0x00FED827"}
Vol- {"Protocol":"NEC","Bits":32,"Data":"0x00FE58A7"}
Chan+ {"Protocol":"NEC","Bits":32,"Data":"0x00FE9867"}
Chan- {"Protocol":"NEC","Bits":32,"Data":"0x00FE18E7"}
Play/Pause {"Protocol":"NEC","Bits":32,"Data":"0x00FE52AD"}
Stop {"Protocol":"NEC","Bits":32,"Data":"0x00FED22D"}
Rwd {"Protocol":"NEC","Bits":32,"Data":"0x00FEE21D"}
FFd {"Protocol":"NEC","Bits":32,"Data":"0x00FE629D"}
Back {"Protocol":"NEC","Bits":32,"Data":"0x00FEA25D"}
Skip {"Protocol":"NEC","Bits":32,"Data":"0x00FE22DD"}
1 {"Protocol":"NEC","Bits":32,"Data":"0x00FE807F"}
2 {"Protocol":"NEC","Bits":32,"Data":"0x00FE40BF"}
3 {"Protocol":"NEC","Bits":32,"Data":"0x00FEC03F"}
4 {"Protocol":"NEC","Bits":32,"Data":"0x00FE20DF"}
5 {"Protocol":"NEC","Bits":32,"Data":"0x00FEA05F"}
6 {"Protocol":"NEC","Bits":32,"Data":"0x00FE609F"}
7 {"Protocol":"NEC","Bits":32,"Data":"0x00FEE01F"}
8 {"Protocol":"NEC","Bits":32,"Data":"0x00FE10EF"}
9 {"Protocol":"NEC","Bits":32,"Data":"0x00FE906F"}
0 {"Protocol":"NEC","Bits":32,"Data":"0x00FE00FF"}
Menu {"Protocol":"NEC","Bits":32,"Data":"0x00FE8877"}
Up {"Protocol":"NEC","Bits":32,"Data":"0x00FE30CF"}
Down {"Protocol":"NEC","Bits":32,"Data":"0x00FEB04F"}
Left {"Protocol":"NEC","Bits":32,"Data":"0x00FEF00F"}
Right {"Protocol":"NEC","Bits":32,"Data":"0x00FE708F"}
OK {"Protocol":"NEC","Bits":32,"Data":"0x00FE08F7"}
Exit {"Protocol":"NEC","Bits":32,"Data":"0x00FEC837"}

Set-top Boxes~

IR Codes for VU+ Duo2~

Example received Code: tele/sonoffIRBridge_1/RESULT = {"IrReceived":{"Protocol":"RC6","Bits":36,"Data":"0x8052900C"}

Example IRsend Command: IRsend {"Protocol":"RC6","Bits":36,"Data":0x8052900C}

  • 0xC8052900C="ON/OFF"
  • 0xC8052100D="Mute"
  • 0xC80529010="Vol+"
  • 0xC80521011="Vol-"
  • 0xC80529020="P+"
  • 0xC80529021="P+"
  • 0xC8052105A="Left"
  • 0xC8052905B="Right"
  • 0xC80529058="Up"
  • 0xC80529059="Down"
  • 0xC8052905C="OK"
  • 0xC80529054="Men"
  • 0xC8052906D="Red"
  • 0xC8052906F="Yellow"
  • 0xC8052906E="Green"
  • 0xC80529070="Blue"
  • 0xC8052902D="Play/Pause"
  • 0xC80529031="Stop"
  • 0xC80529028="Forward"
  • 0xC80529029="Back"
  • 0xC80529001="Key 1"
  • 0xC80529002="Key 2"
  • 0xC80529003="Key 3"
  • 0xC80529004="Key 4"
  • 0xC80529005="Key 5"
  • 0xC80529006="Key 6"
  • 0xC80529007="Key 7"
  • 0xC80529008="Key 8"
  • 0xC80529009="Key 9"
  • 0xC80529000="Key 0"
  • 0xC805290BB="Key <"
  • 0xC805290BC="Key >"
  • 0xC80529037="Record"
  • 0xC805290CC="EPG"
  • 0xC80529055="Exit"
  • 0xC805290E5="Audio"
  • 0xC805290F2="Radio"
  • 0xC805290E4="TV"
  • 0xC80521049="Context"
  • 0xC80529081="Help"

IR Remote Codes AppleTV Gen4~

Up {"Protocol":"NEC","Bits":32,"Data":"0x77E15080"}

Down {"Protocol":"NEC","Bits":32,"Data":"0x77E13080"}

Left {"Protocol":"NEC","Bits":32,"Data":"0x77E19080"}

Right {"Protocol":"NEC","Bits":32,"Data":"0x77E16080"}

Ok {"Protocol":"NEC","Bits":32,"Data":"0x77E13A80"}

Menu {"Protocol":"NEC","Bits":32,"Data":"0x77E1C080"}

Play/Pause {"Protocol":"NEC","Bits":32,"Data":"0x77E1FA80"}

Home {"Protocol":"NEC","Bits":0,"Data":"0xFFFFFFFFFFFFFFFF"}

IR Remote Codes Humax HMS-1000T DVB-T2 DVR PAL 4-Tune~

Power {"Protocol":"NEC","Bits":32,"Data":"0x000800FF"}

TV Apps {"Protocol":"NEC","Bits":32,"Data":"0x0008D22D"}

Text {"Protocol":"NEC","Bits":32,"Data":"0x00087689"}

Wide {"Protocol":"NEC","Bits":32,"Data":"0x0008728D"}

Play {"Protocol":"NEC","Bits":32,"Data":"0x000816E9"}

Pause {"Protocol":"NEC","Bits":32,"Data":"0x000846B9"}

Rew {"Protocol":"NEC","Bits":32,"Data":"0x0008A659"}

Fwd {"Protocol":"NEC","Bits":32,"Data":"0x000826D9"}

Stop {"Protocol":"NEC","Bits":32,"Data":"0x0008C639"}

Record {"Protocol":"NEC","Bits":32,"Data":"0x00088679"}

Search {"Protocol":"NEC","Bits":32,"Data":"0x0008E21D"}

Plus {"Protocol":"NEC","Bits":32,"Data":"0x000842BD"}

Exit {"Protocol":"NEC","Bits":32,"Data":"0x00086897"}

Back {"Protocol":"NEC","Bits":32,"Data":"0x0008827D"}

Up {"Protocol":"NEC","Bits":32,"Data":"0x00088877"}

Down {"Protocol":"NEC","Bits":32,"Data":"0x0008A857"}

Left {"Protocol":"NEC","Bits":32,"Data":"0x000848B7"}

Right {"Protocol":"NEC","Bits":32,"Data":"0x000828D7"}

Enter {"Protocol":"NEC","Bits":32,"Data":"0x0008C837"}

Vol + {"Protocol":"NEC","Bits":32,"Data":"0x0008F807"}

Vol - {"Protocol":"NEC","Bits":32,"Data":"0x000802FD"}

Mute {"Protocol":"NEC","Bits":32,"Data":"0x000818E7"}

Home {"Protocol":"NEC","Bits":32,"Data":"0x0008708F"}

Guide {"Protocol":"NEC","Bits":32,"Data":"0x0008D827"}

Ch + {"Protocol":"NEC","Bits":32,"Data":"0x000808F7"}

Ch - {"Protocol":"NEC","Bits":32,"Data":"0x0008F00F"}

Red {"Protocol":"NEC","Bits":32,"Data":"0x000838C7"}

Green {"Protocol":"NEC","Bits":32,"Data":"0x0008B847"}

Yellow {"Protocol":"NEC","Bits":32,"Data":"0x000858A7"}

Blue {"Protocol":"NEC","Bits":32,"Data":"0x00087887"}

1 {"Protocol":"NEC","Bits":32,"Data":"0x0008C03F"}

2 {"Protocol":"NEC","Bits":32,"Data":"0x000820DF"}

3 {"Protocol":"NEC","Bits":32,"Data":"0x0008A05F"}

4 {"Protocol":"NEC","Bits":32,"Data":"0x0008609F"}

5 {"Protocol":"NEC","Bits":32,"Data":"0x0008E01F"}

6 {"Protocol":"NEC","Bits":32,"Data":"0x000810EF"}

7 {"Protocol":"NEC","Bits":32,"Data":"0x0008906F"}

8 {"Protocol":"NEC","Bits":32,"Data":"0x000850AF"}

9 {"Protocol":"NEC","Bits":32,"Data":"0x0008D02F"}

0 {"Protocol":"NEC","Bits":32,"Data":"0x000830CF"}

TV/Radio {"Protocol":"NEC","Bits":32,"Data":"0x0008B04F"}

IR Remote Codes FetchTV Mini(Hybroad H626T)~

Power Toggle {"Protocol":"NEC","Bits":32,"Data":"0x2662BA45"}

Keyboard Select {"Protocol":"NEC","Bits":32,"Data":"0x26624CB3"}

Ch + {"Protocol":"NEC","Bits":32,"Data":"0x26627B84"}

Ch - {"Protocol":"NEC","Bits":32,"Data":"0x2662DB24"}

TV Guide {"Protocol":"NEC","Bits":32,"Data":"0x266207F8"}

Red {"Protocol":"NEC","Bits":32,"Data":"0x2662738C"}

Green {"Protocol":"NEC","Bits":32,"Data":"0x2662936C"}

Yellow {"Protocol":"NEC","Bits":32,"Data":"0x2662E31C"}

Blue {"Protocol":"NEC","Bits":32,"Data":"0x266213EC"}

Info {"Protocol":"NEC","Bits":32,"Data":"0x26628B74"}

Search {"Protocol":"NEC","Bits":32,"Data":"0x26622CD3"}

Apps {"Protocol":"NEC","Bits":32,"Data":"0x2662CC33"}

Menu {"Protocol":"NEC","Bits":32,"Data":"0x26629B64"}

Up {"Protocol":"NEC","Bits":32,"Data":"0x26629B64"}

Down {"Protocol":"NEC","Bits":32,"Data":"0x266223DC"}

Left {"Protocol":"NEC","Bits":32,"Data":"0x2662837C"}

Right {"Protocol":"NEC","Bits":32,"Data":"0x2662C33C"}

Enter {"Protocol":"NEC","Bits":32,"Data":"0x26621CE3"}

Back {"Protocol":"NEC","Bits":32,"Data":"0x2662AB54"}

Exit {"Protocol":"NEC","Bits":32,"Data":"0x266227D8"}

Rew {"Protocol":"NEC","Bits":32,"Data":"0x2662BB44"}

Play/Pause {"Protocol":"NEC","Bits":32,"Data":"0x26625BA4"}

Fwd {"Protocol":"NEC","Bits":32,"Data":"0x26625BA4"}

Stop {"Protocol":"NEC","Bits":32,"Data":"0x2662B34C"}

Record {"Protocol":"NEC","Bits":32,"Data":"0x26624BB4"}

1 {"Protocol":"NEC","Bits":32,"Data":"0x266240BF"}

2 {"Protocol":"NEC","Bits":32,"Data":"0x2662C03F"}

3 {"Protocol":"NEC","Bits":32,"Data":"0x266220DF"}

4 {"Protocol":"NEC","Bits":32,"Data":"0x2662A05F"}

5 {"Protocol":"NEC","Bits":32,"Data":"0x2662609F"}

6 {"Protocol":"NEC","Bits":32,"Data":"0x2662E01F"}

7 {"Protocol":"NEC","Bits":32,"Data":"0x266210EF"}

8 {"Protocol":"NEC","Bits":32,"Data":"0x2662906F"}

9 {"Protocol":"NEC","Bits":32,"Data":"0x266250AF"}

0 {"Protocol":"NEC","Bits":32,"Data":"0x2662D02F"}

Smart Receiver VX/CX~


button code
on/off {"Protocol":"NEC","Bits":32,"Data":"0x00FF30CF","DataLSB":"0x00FF0CF3","Repeat":0}
mute {"Protocol":"NEC","Bits":32,"Data":"0x00FFB04F","DataLSB":"0x00FF0DF2","Repeat":0}
FORMAT {"Protocol":"NEC","Bits":32,"Data":"0x00FF1CE3","DataLSB":"0x00FF38C7","Repeat":0}
16:9 {"Protocol":"NEC","Bits":32,"Data":"0x00FFC13E","DataLSB":"0x00FF837C","Repeat":0}
AUDIO {"Protocol":"NEC","Bits":32,"Data":"0x00FFFA05","DataLSB":"0x00FF5FA0","Repeat":0}
1 {"Protocol":"NEC","Bits":32,"Data":"0x00FF807F","DataLSB":"0x00FF01FE","Repeat":0}
2 {"Protocol":"NEC","Bits":32,"Data":"0x00FF40BF","DataLSB":"0x00FF02FD","Repeat":0}
3 {"Protocol":"NEC","Bits":32,"Data":"0x00FFC03F","DataLSB":"0x00FF03FC","Repeat":0}
4 {"Protocol":"NEC","Bits":32,"Data":"0x00FF20DF","DataLSB":"0x00FF04FB","Repeat":0}
5 {"Protocol":"NEC","Bits":32,"Data":"0x00FFA05F","DataLSB":"0x00FF05FA","Repeat":0}
6 {"Protocol":"NEC","Bits":32,"Data":"0x00FF609F","DataLSB":"0x00FF06F9","Repeat":0}
7 {"Protocol":"NEC","Bits":32,"Data":"0x00FFE01F","DataLSB":"0x00FF07F8","Repeat":0}
8 {"Protocol":"NEC","Bits":32,"Data":"0x00FF10EF","DataLSB":"0x00FF08F7","Repeat":0}
9 {"Protocol":"NEC","Bits":32,"Data":"0x00FF906F","DataLSB":"0x00FF09F6","Repeat":0}
TV/R {"Protocol":"NEC","Bits":32,"Data":"0x00FFA25D","DataLSB":"0x00FF45BA","Repeat":0}
0 {"Protocol":"NEC","Bits":32,"Data":"0x00FF00FF","DataLSB":"0x00FF00FF","Repeat":0}
RECALL {"Protocol":"NEC","Bits":32,"Data":"0x00FF19E6","DataLSB":"0x00FF9867","Repeat":0}
volume + {"Protocol":"NEC","Bits":32,"Data":"0x00FF5AA5","DataLSB":"0x00FF5AA5","Repeat":0}
volume - {"Protocol":"NEC","Bits":32,"Data":"0x00FFDA25","DataLSB":"0x00FF5BA4","Repeat":0}
P - {"Protocol":"NEC","Bits":32,"Data":"0x00FFBA45","DataLSB":"0x00FF5DA2","Repeat":0}
P + {"Protocol":"NEC","Bits":32,"Data":"0x00FF3AC5","DataLSB":"0x00FF5CA3","Repeat":0}
red {"Protocol":"NEC","Bits":32,"Data":"0x00FFEC13","DataLSB":"0x00FF37C8","Repeat":0}
green {"Protocol":"NEC","Bits":32,"Data":"0x00FF6C93","DataLSB":"0x00FF36C9","Repeat":0}
yellow {"Protocol":"NEC","Bits":32,"Data":"0x00FF4CB3","DataLSB":"0x00FF32CD","Repeat":0}
blue {"Protocol":"NEC","Bits":32,"Data":"0x00FF2CD3","DataLSB":"0x00FF34CB","Repeat":0}
MENU {"Protocol":"NEC","Bits":32,"Data":"0x00FF4AB5","DataLSB":"0x00FF52AD","Repeat":0}
BACK {"Protocol":"NEC","Bits":32,"Data":"0x00FF44BB","DataLSB":"0x00FF22DD","Repeat":0}
EXIT {"Protocol":"NEC","Bits":32,"Data":"0x00FFB44B","DataLSB":"0x00FF2DD2","Repeat":0}
up {"Protocol":"NEC","Bits":32,"Data":"0x00FF04FB","DataLSB":"0x00FF20DF","Repeat":0}
left {"Protocol":"NEC","Bits":32,"Data":"0x00FF8877","DataLSB":"0x00FF11EE","Repeat":0}
OK {"Protocol":"NEC","Bits":32,"Data":"0x00FFEA15","DataLSB":"0x00FF57A8","Repeat":0}
right {"Protocol":"NEC","Bits":32,"Data":"0x00FF08F7","DataLSB":"0x00FF10EF","Repeat":0}
down {"Protocol":"NEC","Bits":32,"Data":"0x00FF847B","DataLSB":"0x00FF21DE","Repeat":0}
INFO {"Protocol":"NEC","Bits":32,"Data":"0x00FF34CB","DataLSB":"0x00FF2CD3","Repeat":0}
FAV {"Protocol":"NEC","Bits":32,"Data":"0x00FF8679","DataLSB":"0x00FF619E","Repeat":0}
TXT {"Protocol":"NEC","Bits":32,"Data":"0x00FF7A85","DataLSB":"0x00FF5EA1","Repeat":0}
EPG {"Protocol":"NEC","Bits":32,"Data":"0x00FF06F9","DataLSB":"0x00FF609F","Repeat":0}
FR {"Protocol":"NEC","Bits":32,"Data":"0x00FF8976","DataLSB":"0x00FF916E","Repeat":0}
REC {"Protocol":"NEC","Bits":32,"Data":"0x00FF619E","DataLSB":"0x00FF8679","Repeat":0}
FF {"Protocol":"NEC","Bits":32,"Data":"0x00FF29D6","DataLSB":"0x00FF946B","Repeat":0}
PREV {"Protocol":"NEC","Bits":32,"Data":"0x00FF09F6","DataLSB":"0x00FF906F","Repeat":0}
Play/Pause {"Protocol":"NEC","Bits":32,"Data":"0x00FFE916","DataLSB":"0x00FF9768","Repeat":0}
NEXT {"Protocol":"NEC","Bits":32,"Data":"0x00FFC936","DataLSB":"0x00FF936C","Repeat":0}
(none left) {"Protocol":"NEC","Bits":32,"Data":"0x00FF4BB4","DataLSB":"0x00FFD22D","Repeat":0}
STOP {"Protocol":"NEC","Bits":32,"Data":"0x00FF718E","DataLSB":"0x00FF8E71","Repeat":0}
(none right) {"Protocol":"NEC","Bits":32,"Data":"0x00FF8976","DataLSB":"0x00FF916E","Repeat":0}
USB {"Protocol":"NEC","Bits":32,"Data":"0x00FFFB04","DataLSB":"0x00FFDF20","Repeat":0}
HELP {"Protocol":"NEC","Bits":32,"Data":"0x00FF54AB","DataLSB":"0x00FF2AD5","Repeat":0}
DVD {"Protocol":"NEC","Bits":32,"Data":"0x00FFDB24","DataLSB":"0x00FFDB24","Repeat":0}

BD/DVD players~

ä IR Remote Codes Sony BD-S1500~

Common buttons should work across multiple models

Eject {"Protocol":"SONY","Bits":20,"Data":"0x68B47"}

Power {"Protocol":"SONY","Bits":20,"Data":"0xA8B47"}

Red {"Protocol":"SONY","Bits":20,"Data":"0xE6B47"}

Green {"Protocol":"SONY","Bits":20,"Data":"0x16B47"}

Yellow {"Protocol":"SONY","Bits":20,"Data":"0x96B47"}

Bue {"Protocol":"SONY","Bits":20,"Data":"0x66B47"}

Top Menu {"Protocol":"SONY","Bits":20,"Data":"0x34B47"}

Popup/Menu {"Protocol":"SONY","Bits":20,"Data":"0x94B47"}

Return {"Protocol":"SONY","Bits":20,"Data":"0xC2B47"}

Options {"Protocol":"SONY","Bits":20,"Data":"0xFCB47"}

Home {"Protocol":"SONY","Bits":20,"Data":"0x42B47"}

Up {"Protocol":"SONY","Bits":20,"Data":"0x9CB47"}

Down {"Protocol":"SONY","Bits":20,"Data":"0x5CB47"}

Left {"Protocol":"SONY","Bits":20,"Data":"0xDCB47"}

Right {"Protocol":"SONY","Bits":20,"Data":"0x3CB47"}

Enter {"Protocol":"SONY","Bits":20,"Data":"0xBCB47"}

Favourites {"Protocol":"SONY","Bits":20,"Data":"0xBCB47"}

Netflix {"Protocol":"SONY","Bits":20,"Data":"0xD2B47"}

Play {"Protocol":"SONY","Bits":20,"Data":"0x58B47"}

Pause {"Protocol":"SONY","Bits":20,"Data":"0x98B47"}

Rew {"Protocol":"SONY","Bits":20,"Data":"0xD8B47"}

Fwd {"Protocol":"SONY","Bits":20,"Data":"0x38B47"}

Prev {"Protocol":"SONY","Bits":20,"Data":"0xEAB47"}

Next {"Protocol":"SONY","Bits":20,"Data":"0x6AB47"}

Stop {"Protocol":"SONY","Bits":20,"Data":"0x18B47"}

Subtitles {"Protocol":"SONY","Bits":20,"Data":"0xC6B47"}

Audio Track {"Protocol":"SONY","Bits":20,"Data":"0x26B47"}

Vol + {"Protocol":"SONY","Bits":12,"Data":"0x490"}

Vol - {"Protocol":"SONY","Bits":12,"Data":"0xC90"}

Mute {"Protocol":"SONY","Bits":12,"Data":"0x290"}

Projectors~

IR Remote Codes Acer K132~

IR Remote Codes for Acer K132 projector (and possibly other models using a remote with Model No. M1820)

ON/OFF   {"Protocol":"NEC","Bits":32,"Data":"0x10C8E11E"}
+* Freeze    {"Protocol":"NEC","Bits":32,"Data":"0x10C8718E"}
+* Hide  {"Protocol":"NEC","Bits":32,"Data":"0x10C8F10E"}
+* Ratio {"Protocol":"NEC","Bits":32,"Data":"0x10C806F9"}
+* Zoom  {"Protocol":"NEC","Bits":32,"Data":"0x10C8D12E"}
+* Mode  {"Protocol":"NEC","Bits":32,"Data":"0x10C801FE"}
+* Source    {"Protocol":"NEC","Bits":32,"Data":"0x10C831CE"}
+
+* Back  {"Protocol":"NEC","Bits":32,"Data":"0x10C832CD"}
+* Up    {"Protocol":"NEC","Bits":32,"Data":"0x10C841BE"}
+* Down  {"Protocol":"NEC","Bits":32,"Data":"0x10C8A15E"}
+* Left  {"Protocol":"NEC","Bits":32,"Data":"0x10C8C13E"}
+* Right {"Protocol":"NEC","Bits":32,"Data":"0x10C8817E"}
+* Enter {"Protocol":"NEC","Bits":32,"Data":"0x10C8B24D"}
+* Menu  {"Protocol":"NEC","Bits":32,"Data":"0x10C821DE"}
+
+* Vol+  {"Protocol":"NEC","Bits":32,"Data":"0x10C8C639"}
+* Vol-  {"Protocol":"NEC","Bits":32,"Data":"0x10C826D9"}
+
+* Sound {"Protocol":"NEC","Bits":32,"Data":"0x10C8AD52"}
+* Mute  {"Protocol":"NEC","Bits":32,"Data":"0x10C88679"}
+

Soundbars~

IR Codes Soundbar Panasonic SCALL70T~

Example received Code: tele/sonoffIRBridge_1/RESULT = {"IrReceived":{"Protocol":"PANASONIC","Bits":48,"Data":"0x40040500BCB9"}}

Example IRsend Command: IRsend {"Protocol":"PANASONIC","Bits":48,"Data":0x40040500BCB9}

  • 0x40040500BCB9="ON/OFF"
  • 0x400405004C49="Mute"
  • 0x400405000401="Vol+"
  • 0x400405008481="Vol-"
  • 0x40040538DFE2="OK"
  • 0x400405383F02="Up"
  • 0x40040538BF82="Down"
  • 0x40040538AD90="Setup"
  • 0x400405280D20="Sound"
  • 0x400405380538="Bluetooth"
  • 0x400405006164="Input"

IR Codes Soundcore Infini Pro~

Example received Code: tele/sonoffIRBridge_1/RESULT = {"IrReceived":{"Protocol":"NEC","Bits":32,"Data":"0xFD256897"}}

Example IRsend Command: {"Protocol":"NEC","Bits":32,"Data":"0xFD256897"}

  • 0xFD2502FD="ON/OFF"
  • 0xFD2518E7="Mute"
  • 0xFD2508F7="Input"
  • 0xFD259867="Bluetooth"
  • 0xFD2548B7="TV"
  • 0xFD256897="Vol+"
  • 0xFD2558A7="Vol-"
  • 0xFD258A75="Previous"
  • 0xFD250AF5="Next"
  • 0xFD25C837="Play/Pause"
  • 0xFD2554AB="Movie"
  • 0xFD255CA3="Music"
  • 0xFD2552AD="Voice"
  • 0xFD2532CD="Bass-"
  • 0xFD258877="Bass+"
  • 0xFD2538C7="Surround"

IR Codes Goodmans GDSBT1000P~

Example received Code: RESULT = {"IrReceived":{"Protocol":"NEC","Bits":32,"Data":"0x4FBD02F","DataLSB":"0x20DF0BF4","Repeat":0}}

Example IRsend Command: {"Protocol":"NEC","Bits":32,"Data":"0x4FB30CF"}

  • 0x4FB30CF="ON/OFF"
  • 0x20DF08F7="Mute"
  • 0x20DF0FF0="Mode Bluetooth"
  • 0x20DF0AF5="Mode Optical"
  • 0x20DF16E9="Mode Coaxial"
  • 0x20DF07F8="Mode Line in"
  • 0x20DF09F6="Mode AUX"
  • 0x20DF06F9="Bluetooth Pairing"
  • 0x20DF10EF="Music"
  • 0x20DF0BF4="Movie"
  • 0x20DF14EB="Voice"
  • 0x20DF00FF="Treble +"
  • 0x20DF03FC="Treble -"
  • 0x20DF01FE="Bass +"
  • 0x20DF04FB="Bass -"
  • 0x20DF02FD="Volume +"
  • 0x20DF05FA="Volume -"
  • 0x20DF11EE="Previous"
  • 0x20DF12ED="Play/Pause"
  • 0x20DF13EC="Next"

Vacuum Cleaners~

IR Codes Vacuum Cleaner Ecovacs Deebot Slim2~

Example received Code: tele/sonoffIRBridge_1/RESULT = {"IrReceived":{"Protocol":"NEC","Bits":32,"Data":"0x00FFD02F"}}

Example IRsend Command: IRsend {"Protocol":"NEC","Bits":32,"Data":0x00FFD02F}

  • 0x00FFD02F="Automatic Mode/Pause"
  • 0x00FF609F="Edge Cleaning"
  • 0x00FF40BF="Spot Cleaning"
  • 0x00FF708F="Back to Charging Base"
  • 0x00FFC837="Forward"
  • 0x00FFE01F="Left"
  • 0x00FFF00F="Right"
  • 0x00FFC03F="Back/Turn around"

Ventilation~

IR Codes Prana 150 energy recovery ventilation~

See device https://prana.org.ua/models/prana_150 (Ukrainian)

Example received Code: MQT: tele/sonoffir/RESULT = {"IrReceived":{"Protocol":"NEC","Bits":32,"Data":"0x00FF00FF"}}

Example IRsend Command: IRsend {"Protocol":"NEC","Bits":32,"Data":"0x00FF00FF"}

  • {"Protocol":"NEC","Bits":32,"Data":"0x00FF00FF"}="Power"
  • {"Protocol":"NEC","Bits":32,"Data":"0x00FF807F"}="Screen/LED Brightness"
  • {"Protocol":"NEC","Bits":32,"Data":"0x00FF30CF"}="Heat OFF"
  • {"Protocol":"NEC","Bits":32,"Data":"0x00FF906F"}="Heat ON"
  • {"Protocol":"NEC","Bits":32,"Data":"0x00FF50AF"}="Fan"
  • {"Protocol":"NEC","Bits":32,"Data":"0x00FFA857"}="Anti freeze"
  • {"Protocol":"NEC","Bits":32,"Data":"0x00FFB04F"}="Night Mode"
  • {"Protocol":"NEC","Bits":32,"Data":"0x00FF708F"}="Fan -"
  • {"Protocol":"NEC","Bits":32,"Data":"0x00FF28D7"}="Fan +"
  • {"Protocol":"NEC","Bits":32,"Data":"0x00FF609F"}="Night Mode Fan -"
  • {"Protocol":"NEC","Bits":32,"Data":"0x00FF10EF"}="Night Mode Fan +"

image

Christmas candle - Weihnachtsbeleuchtung~

Krinner Lumix IR Remote~

Remote control has two buttons and three channels. Button 1 is to switch on Button 0 is to switch off Double click on button 1 is flicker mode No usable protocol found yet, but raw mode does it.

Example for HttpGetRequest and irsend sendHttpGetRequest("http://192.168.1.234/cm?cmnd=irsend5%200,2000,1000,%20400,1000,%20400,%20400,1000,1000,%20400,1000,%20400,%20400,1000,%20400,1000,%20400,1000,%20400,2000,5600")

  • off channel A irsend 0,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,1000, 400,1000, 400,1000, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,1000, 400,1000, 400,1000, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,1000, 400,1000, 400,1000, 400,2000,5600
  • on channel A irsend 0,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,2000,5600
  • flicker channel A irsend 0,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,1000, 400,1100,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,1000, 400,1100,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400, 400,1000, 400,1100,1000, 400, 400,2000,5600

  • off channel B irsend 0,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400, 400,1000, 400,1000, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400, 400,1000, 400,1000, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400, 400,1000, 400,1000, 400,2000,5600

  • on channel B irsend 0,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400,1000, 400,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400,1000, 400,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400,1000, 400,1000, 400, 400,2000,5600
  • flicker channel B irsend 0,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400, 400,1100,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400, 400,1100,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400, 400,1100,1000, 400, 400,2000,5600

  • off channel C irsend 0,2000,1000, 400,1000, 400, 400,1000,1000, 400, 400,1000,1000, 400, 400,1000, 400,1000, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400, 400,1000,1000, 400, 400,1000, 400,1000, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400, 400,1000,1000, 400, 400,1000, 400,1000, 400,2000,5600

  • on channel C irsend 0,2000,1000, 400,1000, 400, 400,1000,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400, 400,1000,1000, 400,1000, 400,1000, 400, 400,2000,5600
  • flicker channel C irsend 0,2000,1000, 400,1000, 400, 400,1000,1000, 400, 400,1000,1000, 400, 400,1100,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400, 400,1000,1000, 400, 400,1100,1000, 400, 400,2000,5600,2000,1000, 400,1000, 400, 400,1000,1000, 400, 400,1000,1000, 400, 400,1100,1000, 400, 400,2000,5600

Vinkor Flameless Flickering Candles (and clones probably)~

  • on {"Protocol":"NEC","Bits":32,"Data":"0x10ED00FF","Repeat":0}
  • off {"Protocol":"NEC","Bits":32,"Data":"0x10ED40BF","Repeat":0}
  • dim {"Protocol":"NEC","Bits":32,"Data":"0x10ED08F7","Repeat":0}
  • bright {"Protocol":"NEC","Bits":32,"Data":"0x10ED48B7","Repeat":0}

  • candle {"Protocol":"NEC","Bits":32,"Data":"0x10ED30CF","Repeat":0}

  • light {"Protocol":"NEC","Bits":32,"Data":"0x10ED708F","Repeat":0}

  • 2H {"Protocol":"NEC","Bits":32,"Data":"0x10ED20DF","Repeat":0}

  • 4H {"Protocol":"NEC","Bits":32,"Data":"0x10ED609F","Repeat":0}
  • 6H {"Protocol":"NEC","Bits":32,"Data":"0x10ED10EF","Repeat":0}
  • 8H {"Protocol":"NEC","Bits":32,"Data":"0x10ED50AF","Repeat":0}

LED Candles / LED Kerzen~

Duni Warm White LED Candle / Duni Warmweiß LED Kerzen~

  • ON={"Protocol":"NEC","Bits":32,"Data":"0x00FF00FF"}
  • OFF={"Protocol":"NEC","Bits":32,"Data":"0x00FF807F"}
  • 4h={"Protocol":"NEC","Bits":32,"Data":"0x00FF40BF"}
  • 8h={"Protocol":"NEC","Bits":32,"Data":"0x00FFC03F"}
  • ModeCandle={"Protocol":"NEC","Bits":32,"Data":"0x00FF20DF"}
  • ModeLight={"Protocol":"NEC","Bits":32,"Data":"0x00FFA05F"}
  • ModeDark={"Protocol":"NEC","Bits":32,"Data":"0x00FF906F"}
  • ModeBright={"Protocol":"NEC","Bits":32,"Data":"0x00FFE01F"}
  • ModeMoon={"Protocol":"NEC","Bits":32,"Data":"0x00FF10EF"}
  • ModeNightLight={"Protocol":"NEC","Bits":32,"Data":"0x00FF609F"}

Duni Multicoloured LED Candle / Duni Mehrfarbige LED Kerzen~

  • ON_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F48B7","DataLSB":"0x01FE12ED"}
  • OFF_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F807F","DataLSB":"0x01FE01FE"}
  • ModeSmooth_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F58A7","DataLSB":"0x01FE1AE5"}
  • ModeNightLight_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F7887","DataLSB":"0x01FE1EE1"}
  • ModeCandle_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F40BF","DataLSB":"0x01FE02FD"}
  • ModeLight_COL={"Protocol":"NEC","Bits":32,"Data":"0x807FC03F","DataLSB":"0x01FE03FC"}
  • ModeTimer_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F20DF","DataLSB":"0x01FE04FB"}
  • ModeDark_COL={"Protocol":"NEC","Bits":32,"Data":"0x807FA05F","DataLSB":"0x01FE05FA"}
  • ModeBright_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F609F","DataLSB":"0x01FE06F9"}
  • ColorRed_COL={"Protocol":"NEC","Bits":32,"Data":"0x807FE01F","DataLSB":"0x01FE07F8"}
  • ColorGreen_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F10EF","DataLSB":"0x01FE08F7"}
  • ColorBlue_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F906F","DataLSB":"0x01FE09F6"}
  • ColorOrange_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F50AF","DataLSB":"0x01FE0AF5"}
  • ColorLightGreen_COL={"Protocol":"NEC","Bits":32,"Data":"0x807FD827","DataLSB":"0x01FE1BE4"}
  • ColorLightBlue_COL={"Protocol":"NEC","Bits":32,"Data":"0x807FF807","DataLSB":"0x01FE1FE0"}
  • ColorViolet_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F30CF","DataLSB":"0x01FE0CF3"}
  • ColorYellow_COL={"Protocol":"NEC","Bits":32,"Data":"0x807FB04F","DataLSB":"0x01FE0DF2"}
  • ColorBlueWhite_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F708F","DataLSB":"0x01FE0EF1"}
  • ColorPink_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F00FF","DataLSB":"0x01FE00FF"}
  • ColorYellowWhite_COL={"Protocol":"NEC","Bits":32,"Data":"0x807FF00F","DataLSB":"0x01FE0FF0"}
  • ColorWhite_COL={"Protocol":"NEC","Bits":32,"Data":"0x807F9867","DataLSB":"0x01FE19E6"}

Fishtec Bougie / Generic Multicolored Led Candle / Generische mehrfarbige LED Kerzen~

  • ON={"Protocol":"NEC","Bits":32,"Data":"0x1FE48B7"}
  • OFF={"Protocol":"NEC","Bits":32,"Data":"0x1FE58A7"}
  • ColorBlue={"Protocol":"NEC","Bits":32,"Data":"0x1FE609F"}
  • ColorRed={"Protocol":"NEC","Bits":32,"Data":"0x1FE20DF"}
  • ColorGreen={"Protocol":"NEC","Bits":32,"Data":"0x1FEA05F"}
  • ColorWhite={"Protocol":"NEC","Bits":32,"Data":"0x1FE30CF"}
  • ColorTurkis={"Protocol":"NEC","Bits":32,"Data":"0x1FE10EF"}
  • ColorOrange={"Protocol":"NEC","Bits":32,"Data":"0x1FE50AF"}
  • ColorPink={"Protocol":"NEC","Bits":32,"Data":"0x1FE708F"}
  • ColorPurple={"Protocol":"NEC","Bits":32,"Data":"0x1FEF807"}
  • ColorLightPurple={"Protocol":"NEC","Bits":32,"Data":"0x1FE906F"}
  • ColorLightBlue={"Protocol":"NEC","Bits":32,"Data":"0x1FED827"}
  • ColorOceanBlue={"Protocol":"NEC","Bits":32,"Data":"0x1FEB04F"}
  • ModeMultiColor={"Protocol":"NEC","Bits":32,"Data":"0x1FEC03F"}
  • ModeSwitch={"Protocol":"NEC","Bits":32,"Data":"0x1FE7887"}

Edifier R1850DB IR remote~

  • MUTE: {"Protocol":"NEC","Bits":32,"Data":"0x8E7827D","DataLSB":"0x10E741BE","Repeat":0}
  • +: {"Protocol":"NEC","Bits":32,"Data":"0x8E7609F","DataLSB":"0x10E706F9","Repeat":0}
  • -: {"Protocol":"NEC","Bits":32,"Data":"0x8E7E21D","DataLSB":"0x10E747B8","Repeat":0}
  • ON/OFF: {"Protocol":"NEC","Bits":32,"Data":"0x8E7629D","DataLSB":"0x10E746B9","Repeat":0}
  • PC: {"Protocol":"NEC","Bits":32,"Data":"0x8E7E01F","DataLSB":"0x10E707F8","Repeat":0}
  • AUX: {"Protocol":"NEC","Bits":32,"Data":"0x8E7906F","DataLSB":"0x10E709F6","Repeat":0}
  • OPT: {"Protocol":"NEC","Bits":32,"Data":"0x8E7A25D","DataLSB":"0x10E745BA","Repeat":0}
  • COX: {"Protocol":"NEC","Bits":32,"Data":"0x8E7C03F","DataLSB":"0x10E703FC","Repeat":0}
  • BT: {"Protocol":"NEC","Bits":32,"Data":"0x8E73AC5","DataLSB":"0x10E75CA3","Repeat":0}
  • <<: {"Protocol":"NEC","Bits":32,"Data":"0x8E77887","DataLSB":"0x10E71EE1","Repeat":0}
  • ||: {"Protocol":"NEC","Bits":32,"Data":"0x8E77A85","DataLSB":"0x10E75EA1","Repeat":0}

  • : {"Protocol":"NEC","Bits":32,"Data":"0x8E740BF","DataLSB":"0x10E702FD","Repeat":0}

\ No newline at end of file diff --git a/Commands/index.html b/Commands/index.html new file mode 100644 index 0000000000..1737636055 --- /dev/null +++ b/Commands/index.html @@ -0,0 +1,10 @@ + Commands - Tasmota
Skip to content

Commands

Tasmota provides a powerful control interface using commands

Commands can be issued using MQTT, web requests, webUI console and serial

How to use commands~

Every command used without a parameter (payload) returns the current setting.

Power returns the status of first defined power output (usually Relay1)

Instead of 0 you can use off or false and instead of 1 you can use on or true.

Power ON turns first defined power output (usually Relay1) on

Power1 1 also turns first defined power output (usually Relay1) on

Power1 True also turns first defined power output (usually Relay1) on

Replace <x> in a command with the appropriate index number. Leave it empty to use the first available.

Power1 and Power both control first defined power output (usually Relay1)

In commands with x..y value parameters use a number from x to y range.

When a command mentions resetting to "firmware default" it means the setting will revert to the one in the flashed binary file. If you used user_config_override.h at compile time it will revert to those.

Note

All commands are standard in the form: COMMAND<INDEX> DATA It has only one SPACE between INDEX and DATA. (There is no command in Tasmota that allows the = sign)

Example

Set networking IP (XXX.XXX.XXX.XXX) addresses using the command IPAddress<x>: IPAddress1 192.168.1.123

The = sign (It can be understood as the sign :) is used only for meaning explanation reference: IPAddress1 = set device IP address

Note

Beside results initiated by a command (synchronous) you can get asynchronous results initiated by rule trigger, telemetry event, commands from other source or changed device values. Simply put, other messages may precede messages published as a result of your commands.

Example

A tele/%topic%/STATUS message (sent every 300 seconds by default) may appear exactly after you issue Power off command and before you receive stat/%topic%/RESULT = {"POWER":"OFF"} message.

with MQTT~

To send commands and view responses you'll need an MQTT client.

Commands over MQTT are issued by using topic cmnd/%topic%/<command> and payload <parameter> where %topic% is the topic of the device you're sending the command to. If there is no <parameter> (an empty MQTT message/payload), a query is sent for current status of the <command>.

See MQTT article to find out more.

with Web Requests~

Commands can be executed via web (HTTP) requests, for example:

http://<ip>/cm?cmnd=Power%20TOGGLE
+http://<ip>/cm?cmnd=Power%20On
+http://<ip>/cm?cmnd=Power%20off
+http://<ip>/cm?user=admin&password=joker&cmnd=Power%20Toggle
+

Any spaces or special characters must be replaced with their ASCII hex codes.

You must precede each hex code with %. Most used codes are: space = %20 and ; = %3B.

Tip

Use URLencoder.org to easily convert your commands.

If you have set a password for web user interface access, this must be included (in plaintext) in the URL of the HTTP request, like so:

http://<ip>/cm?user=<username>&password=<password>&cmnd=Power%20On
+

in Console in the Web UI~

Console menu in the web UI is a convenient place to send commands and it behaves similar to a terminal connection via serial bridge.

Warning

The GUI controls do not and can not have all the features and commands implemented. For precise and complete control use Console commands!

over Serial Bridge~

If you flashed the device via serial method you can connect to it with a terminal application (e.g. Termite or Arduino IDE Serial Monitor) to issue commands and follow responses. This is a practical way to do a Backlog setup of your new device.

Serial interface is set to 115200 bps except for devices that require a different baud rate

the Power of Backlog~

Backlog command allows executing up to 30 consecutive commands with a single command line. Each command is separated by a semicolon (";"). Backlog is a useful feature to avoid numerous restarts when setting up a new device. You can use it to:

Set up both Wi-Fi AP's

Backlog SSID1 <myssid>; Password1 <mypassword>; SSID2 <myssid2>; Password2 <mypassword2>
+
Configure MQTT broker address, MQTT credentials, device topic and activate a few custom options
Backlog MqttHost <yourhost>; MqttUser <user>; MqttPassword <password>; Topic <customtopic>; SetOption53 1; PowerRetain on
+
For specific power control, using backlog like a script
Backlog Status 1; Power2 on; Delay 20; Power2 off; Status 4
+
When using web requests (Don't forget to encode "space" as '%20' and ";" as '%3B')
http://<ip>/cm?user=admin&password=joker&cmnd=Backlog%20Power%20Toggle%3BPower1%20off
+

A Backlog command without an argument clears a possible existing Backlog queue.

Example

in case of command Backlog Power1 OFF; Delay 600; Power1 ON the usage of an additional Backlog command without any argument within the delay time of 1 minute will delete the whole queue Power1 OFF; Delay 600; Power1 ON. Therefore Power1 ON command will not be executed and the power would remain off.

Commands List~

Warning

If you're using Tasmota versions earlier current release some of the commands might not work. Availability of some features and their associated commands depend on the firmware build. Please consult the builds table for a reference of which features are available for each firmware variant.

Note

Almost all settings using string parameters (except Rule and MqttFingerprint) share a common area with max 698 chars, i.e. the total length of all these parameters is limited to this size (you will be noted if this limit is exceeded).

Control~

Command Parameters
Backlog List of commands to be executed in sequence separated by ;
See Using Backlog for examples.
Backlog0 List of commands to be executed without any delay in sequence separated by ;
See Using Backlog for examples.
BlinkCount Number of relay toggles (blinks) (does not control the status LED)
0 = blink many times before restoring power state
1..32000 = set number of blinks (default = 10)
BlinkTime 2..3600 set duration, in 0.1 second increments, to blink aka toggle Power (does not control the status LED)
Br Run the code from the console
Example to download a file from a remote server into filesystem:
br def urlfetch(url,file); if file==nil; import string; file=string.split(url,'/').pop(); end; var wc=webclient(); wc.begin(url); var st=wc.GET(); if st!=200 raise 'connection_error','status: '+str(st) end; st='Fetched '+str(wc.write_file(file)); print(url,st); wc.close(); return st; end; urlfetch('https://raw.githubusercontent.com/arendst/Tasmota/development/tasmota/zigbee/giex_water.zb')
ButtonDebounce User control over button debounce timing
40..1000 = set button debounce time in milliseconds (default = 50)
Buzzer 0 = stop active buzzer cycle
<count>,<beep>,<silence>,<tune> = read more...
2,3 = Beep twice with 300 milliseconds duration and 100 milliseconds pause
2,3,4 = Beep twice with 300 milliseconds duration and 400 milliseconds pause
1,2,3,0xF54 (0000 0000 0000 0000 0000 1111 0101 0100). Each 1 bit beeps for 200 milliseconds and each bounded 0 bit pauses for 300 milliseconds
-1 = infinite mode
-2 = follow LED mode
BuzzerActive
SetOption67
iFan03 Buzzer control
0 = disable Sonoff iFan03 buzzer (default)
1 = enable Sonoff iFan03 buzzer
BuzzerPwm
SetOption111
0 = (default)
1 = use frequency output for buzzer pin instead of on/off signal, for piezo buzzers
DevGroupName<x> 0 = clear device group <x> name and restart
<value> = set device group name and restart.\
If a device group name is not set for a group, the MQTT group topic (GroupTopic) is used (with the device group number appended for device group numbers > 1).
DevGroupSend<x> <item> = <value>[ ...] = send an update to device group <x>. The device group name must have been previously set with DevGroupName<x>. Multiple item/value pairs can be specified separated by a space. Spaces in <value> must be escaped with a backslash (\). The message sent is also processed on the local device as if it had been received from the network.

For items with numeric values, <value> can be specified as @<operator>[<operand>] to send a value after performing an operation on the current value. <operator> can be + (add), - (subtract), ^ (invert), & (bitwise AND) or | (bitwise OR). If <operand> is not specified, it defaults to 0xffffffff for the invert operator and 1 for other operators.

To indicate that an item should not be shared with the group until changed again, prefix the value with N.

3 = Light fade (0 = Off, 1 = On)
4 = Light speed (1..40)
5 = Light brightness (0..255)
6 = Light Scheme
7 = Light fixed color (0 = white (using CT channels), other values according to Color)
8 = PWM dimmer low preset (0..255)
9 = PWM dimmer high preset (0..255)
10 = PWM dimmer power-on brightness (0..255)
128 = Relay Power - bitmask with bits set for relays to be powered on. The number of relays can be specified in bits 24 - 31. If the number of relays is not specified, only relay 1 is set
129 = No Status Share - DevGroupShare bitmask indicating which items should not be shared until changed.
192 = Event - event name and arguments
193 = Command - command and arguments
224 = Light channels - comma separated list of brightness levels (0..255) for channels 1 - 5 (e.g. 255,128,0,0,0 will turn the red channel on at 100% and the green channel on at 50% on an RGB light) or hex color value (#RRGGBB, #RRGGBBWW, etc.)

Examples:
DevGroupSend 5=90 128=1 - send an update to set the light brightness to 90 and turn relay 1 on.
DevGroupSend 193=Buzzer\ 2,3 - send the Buzzer 2,3 command.
DevGroupSend 6=@+ 5=@-10 - set the next fixed color and decrease the brightness by 10.
DevGroupSend 128=^ - toggle all the relays.
DevGroupSend 224=NFF0000 - set the color to red locally and inform the group that light channel information is not to be shared until changed.
DevGroupSend 129=@\|18 - do not share light brightness or channel status until changed.
DevGroupShare <in>,<out> = set incoming and outgoing shared items (default = 0xffffffff,0xffffffff)
<in> and <out> are bit masks where each mask is the sum of the values for the categories (listed below) to be shared. For example, to receive only power (1), light brightness (2) and light color (16) and send only power (1), enter the command DevGroupShare 19,1.

1 = Power
2 = Light brightness
4 = Light fade/speed
8 = Light scheme
16 = Light color
32 = Dimmer settings (presets)
64 = Event
DevGroupStatus<x> Show the status of device group <x> including a list of the currently known members.
DevGroupTie<x> <relay> = Tie the relay to the device group <x>. Only applies when option 88 is enabled.
FanSpeed Fan speed control (iFan02/iFan03 only)
0 = turn fan OFF
1..3 = set fan speed
+ = increase fan speed
- = decrease fan speed
Interlock Relay interlock mode and group selection.
0 = disable relay interlock for all relays (i.e., each relay is self-locking) (default)
1 = set interlock mode for selected relays
Add up to 8 relays in 1 to 4 interlock groups, each separated by a space. For example
1,2 3,4 = Group Relay1 and Relay2 in group 1 and Relay3 and Relay4 in group 2 (note the space between the two groups)
1,2,3 = group Relay1, Relay2 and Relay3 in a single interlock group
1 3 2,4 = Relay1 is in group 1, Relay3 in group 2, Relay2 and Relay4 in group 3
Json Input any command as valid JSON
{<Tasmota commands>}
example: cmnd/tasmota/json {"HSBColor":"360,100,100","Scheme": 1,"Dimmer": 10,"CT": 220}
LedMask Set a bitmask specifying which relays control the LED indicator. Read more...
<bitmask> = bitwise value representing each relay. Values may be entered as either hexadecimal or decimal values (e.g., 0xFFFF = 65535).
0xFFFF (= 1111 1111 1111 1111) All relays control the power LED (default)
LedState must be enabled (i.e., != 0) in order for LedMask to take effect.
LedPower LED power state as on or off
0 = turn LED OFF and set LedState 0
1 = turn LED ON and set LedState 8
2 = toggle LED and set LedState 0
(Use Backlog LedPower 0; SetOption31 1 to disable LED even when Wi-Fi or MQTT is not connected)
LedPower<x> LED<x> power state control. Enabled only when LedLink(i) is configured
0 = turn LED OFF and set LedState 0
1 = turn LED ON and set LedState 0
2 = toggle LED and set LedState 0
LedState Manage LED state
0 = disable use of LED as much as possible
1 = show power state on LED (LED on when power on) (default) (inverted for Sonoff Touch/T1)
2 = show MQTT subscriptions as a LED blink
3 = show power state and MQTT subscriptions as a LED blink
4 = show MQTT publications as a LED blink
5 = show power state and MQTT publications as a LED blink
6 = show all MQTT messages as a LED blink
7 = show power state and MQTT messages as a LED blink
8 = LED on when Wi-Fi and MQTT are connected.
Cannot be issued directly and is only activated when LedPower is switched from 0 to 1 due to a software function
NoDelay Delay defined by SetOption34 is omitted for any command in a backlog sequence following immediately after NoDelay
This must be used with care, and only for simple commands. Example
Power0 Control the power state simultaneously for all power outputs on the device
0 / off = turn OFF
1 / on = turn ON
2 / toggle = if relay is ON switch to OFF and vice versa
Power<x> Control the corresponding power state (1..8) (also restarts PulseTime)<x>
0 / off / false = turn OFF
1 / on / true = turn ON
2 / toggle = if power state is ON switch to OFF and vice versa
3 / blink = toggle power for BlinkCount times each BlinkTime duration (at the end of blink, power state is returned to pre-blink state)
4 / blinkoff = stop blink sequence and return power state to pre-blink state
PowerOnState Control power state when the device is powered up. More information
0 / OFF = keep power(s) OFF after power up
1 / ON = turn power(s) ON after power up
2 / TOGGLE = toggle power(s) from last saved state
3 = switch power(s) to their last saved state (default)
4 = turn power(s) ON and disable further power control
5 = after a PulseTime period turn power(s) ON (acts as inverted PulseTime mode)
PulseTime<x> Display the amount of PulseTime remaining on the corresponding Relay<x>(x = 0..31)
<value> Set the duration to keep Relay<x> ON when Power<x> ON command is issued. After this amount of time, the power will be turned OFF.
0 / OFF = disable use of PulseTime for Relay<x>
1..111 = set PulseTime for Relay<x> in 0.1 second increments
112..64900 = set PulseTime for Relay<x>, offset by 100, in 1 second increments. Add 100 to desired interval in seconds, e.g., PulseTime 113 = 13 seconds and PulseTime 460 = 6 minutes (i.e., 360 seconds)
Note if you have more than 8 relays:
Defined PulseTime for relays <1-8> will also be active for correspondent Relay <9-16>.
SwitchDebounce User control over switch debounce timing and method
40..1000 = set switch debounce time in milliseconds (default = 50). The granularity is 10 milliseconds, so the normally unnecessary last digit is used by the debouncing code to flag special handling:
0 = no special handling
1 = force_high: only a debounce time long LOW pulse could turn the switch off
2 = force_low: only a debounce time long HIGH pulse could turn the switch on
3 = force_high + force_low
4..8 = unused
9 = AC detection for switches / relays similar to MOES MS-104B / BlitzWolf SS5 etc. If the AC frequency is 50 Hz, SwitchDebounce 69 will turn on the switch after three pulses and off after three missing one.
SwitchMode<x> Switch mode. Index 0 applies to all switches.
0 = toggle (default)
1 = follow (0 = off, 1 = on)
2 = inverted follow (0 = on, 1 = off)
3 = pushbutton (default 1, 0 = toggle)
4 = inverted pushbutton (default 0, 1 = toggle)
5 = pushbutton with hold (default 1, 0 = toggle, Hold = hold)
6 = inverted pushbutton with hold (default 0, 1 = toggle, hold = hold)
7 = pushbutton toggle (0 = toggle, 1 = toggle)
8 = multi change toggle (0 = toggle, 1 = toggle, 2x change = hold)
9 = multi change follow (0 = off, 1 = on, 2x change = hold)
10 = inverted multi change follow (0 = on, 1 = off, 2x change = hold)
11 = pushbutton with dimmer mode
12 = inverted pushbutton with dimmer mode
13 = pushon mode (1 = on, switch off using PulseTime)
14 = inverted pushon mode (0 = on, switch off using PulseTime)
15 = send only an MQTT message on switch change (tele/tasmota/SENSOR with payload {"Time":"2021-01-01T00:00:00","Switch1":"OFF"})
16 = inverted send only an MQTT message on switch change
SwitchText<x> Show current JSON label of Switch<x> (1..8). Only SwitchText shows value for all 8 switches
<text> - replace default Switch<x> label in JSON messages with a custom text
WebButton<x> Change the name of the toggle buttons of the WEB UI. This command accepts spaces in the name
WebQuery<x> Command for GET, POST, PUT, and PATCH HTTP queries, complete with Request Headers and request body (when applicable)
<url> GET|POST|PUT|PATCH [<headers>] <body>
More information...
See also SetOption1 - Set button multipress mode
SetOption11 - Swap pushbutton single and double press functionality
SetOption13 - Allow immediate action on single button press
SetOption26 - Use indexes even when only one relay is present
SetOption31 - Disable Wi-Fi LED status blinking
SetOption32 - Set hold interval before sending HOLD action
SetOption40 - Stop detecting any input change on button GPIO
SetOption67 - Enable/Disable Buzzer
SetOption73 - Decouple buttons from controlling power outputs

Management~

Command Parameters
Delay 2..3600 = set a delay between two backlog commands with 0.1 second increment
-1 = increments to the next second tick
1 = increments with default intercommand delay of 200ms (changeable with SetOption34) Not recommended for precision timing!
DeepSleepTime Time to enter deep sleep mode
0 = disable deep sleep mode (default)
11..86400 = set deep sleep mode time period in seconds
DeviceName Device name displayed in the webUI and used for HA autodiscovery.
<value> = set device name (default = FriendlyName1 value)
DspLine<1|2> For POWR3 Elite and THR3 Elite
2..127 = control message rotation speed on display
DspSpeed For POWR3 Elite and THR3 Elite
<index>,<unit>,<index>,<unit>,... = select message(s) on display
Emulation 0 = disable emulation (default)
1 = enable Belkin WeMo emulation for Alexa
2 = enable Hue Bridge emulation for Alexa
FriendlyName<x> 1 = Reset friendly name to firmware default
<value> = set friendly name (32 char limit)
GPIOs Show list of available components by name and index
255 / All Show list of all components by name and index
GPIO Show current component assignments of the Module's configurable GPIO
255 / All Show component assignments for all the devices available GPIO
GPIO<x> <component> = assign a component to Gpio<x>
I2CScan0 ESP32 only Scan both I2C busses and show addresses for found devices
I2CScan Scan I2C bus and show addresses for found devices
I2CDriver Enable / Disable I2C sensor drivers. Read more...
LogHost 1 = reset syslog host to firmware default (SYS_LOG_HOST)
<value> = set syslog host
LogPort 1 = reset syslog port to firmware default (SYS_LOG_PORT)
2..32766 = set syslog port
Modules Show available modules by name and index
Module Displays active module by name and index
<value> = switch to module <value> and restart
0 = switch to defined template and restart
Module2 Displays active fast reboot fallback module by name and index
<value> = set fast reboot fallback module to <value>
0 = set fast reboot fallback module to defined template
MqttLog 0 = disable logging via MQTT (default)
1 = show only error messages
2 = show error and info messages
3 = show error, info and debug messages
4 = show error, info and more debug messages
NtpServer<x> NTP server setup (x= 1..3)
0 = clear NtpServer<x> settings
1 = reset NtpServer<x> settings to firmware defaults
<value> = set NtpServer<x> host or IP address (32 char limit)
OtaUrl Display current OTA URL
1 = Reset OtaUrl to firmware default
url = set address for OTA (100 char limit)
PWM<x> 0..1023 = set PWM value for channel (NOTE see SetOption15)
PWMFrequency 1 = reset PWM frequency to 223Hz
40..4000 or 2..50000 = set PWM frequency (40Hz to 4kHz on ESP 82xx / 2Hz to 50kHz on ESP32)
As of v8.3.0 the default frequency changed to 977Hz
PWMRange 1 = reset maximum PWM range to 1023
255..1023 = set maximum PWM range
Reset 1 = reset device settings to firmware defaults and restart (see warning below)
2 = erase flash, reset device settings to firmware defaults and restart
3 = erase System Parameter Area in flash (Wi-Fi calibration and related data) and restart (see warning below)
4 = reset device settings to firmware defaults but retain Wi-Fi credentials and restart
5 = erase all flash and reset parameters to firmware defaults but keep Wi-Fi settings and restart
6 = erase all flash and reset parameters to firmware defaults but keep Wi-Fi and MQTT settings and restart
(Erase of flash can take a few seconds to complete and there is no output during the erase process on the serial or web console)
99 = reset device bootcount to zero
⚠ For reset 3and reset 1, device must be power-cycled in order to load new Wifi System parameters.
Restart 1 = restart device with configuration saved to flash
2 = halt system (needs hardware reset or power cycle to restart)
3 = (ESP32 only) restart device into safeboot with configuration saved
9 = save all changes and go into deepsleep waiting for a reset
99 = force restart device without configuration save
For debug and testing stack trace dumps only:
-1 = force an Exception (28) crash
-2 = force a Soft WDT reset (after a freeze of 2 seconds)
-3 = force an OS watchdog reset (after a freeze of 120 seconds, caution!)
RtcNtpServer<x> Use Tasmota NTP server when enabled by define RTC_NTP_SERVER
0 = disabled
1 = enabled
SaveData 0 = save parameter changes only manually, e.g. with Restart 1
1 = save parameter changes every second (default)
2..3600 = save parameter changes every x second
SerialLog Disable hardware serial bridge and
0 = disable serial logging
1 = show only error messages
2 = show error and info messages (default)
3 = show error, info and debug messages
4 = show error, info and more debug messages
SerialLog will be disabled automatically 10 minutes after the device reboots.
SetSensor<x> Enable / Disable individual sensor driver(x= 1..127)
Sleep 0 = turn sleep off
1..250 = set sleep duration in milliseconds to enable energy saving (default = 50)
State Display current device state and publish to %prefix%/%topic%/RESULT topic 
Status = show abbreviated status information
0 = show all status information (1 - 11)
1 = show device parameters information
2 = show firmware information
3 = show logging and telemetry information
4 = show memory information
5 = show network information
6 = show MQTT information
7 = show time information
8 = show connected sensor information (retained for backwards compatibility)
9 = show power thresholds (only on modules with power monitoring)
10 = show connected sensor information (replaces 'Status 8')
11 = show information equal to TelePeriod state message
12 = in case of crash to dump the call stack saved in RT memory
Status0 0 = show all status information in a single line
SysLog 0 = disable syslog logging (default)
1 = show only error messages
2 = show error and info messages
3 = show error, info and debug messages
4 = show error, info and more debug messages
Sunrise 0 = normal, the moment the sun becomes visible/disappears at the horizon (default)
1 = civil, when it begins to be and stops being possible to work outside without artificial light
2 = nautic, when it begins to be and stops being possible to navigate a ship at sea
3 = astronomic, when it stops being / starts being possible to see all of the stars
Template Show current Template
0 = create template from active module
x = create template from a supported module
255 = merge current module and template settings into new template
{ ... } = store template in a JSON payload
Does not activate the template. To activate use Module 0.
Time 0 = enable NTP (default)
1 = format JSON message timestamp in ISO format
2 = format JSON message timestamp in both ISO and Epoch format
3 = format JSON message timestamp in Epoch format
4 = format JSON message timestamp in milliseconds
<value> = disable NTP and set UTC time as epoch value if greater than 1451602800 (January 1, 2016)
TimeStd
TimeDst
Set policies for the beginning of daylight saving time (DST) and return back to standard time (STD) 
Use the Tasmota timezone table to find the commands for your time zone.
0 = reset parameters to firmware defaults
H,W,M,D,h,T
H = hemisphere (0 = northern hemisphere / 1 = southern hemisphere)
W = week (0 = last week of month, 1..4 = first .. fourth)
M = month (1..12)
D = day of week (1..7 1 = Sunday 7 = Saturday)
h = hour (0..23) in local time
T = time zone (-780..780) (offset from UTC in MINUTES - 780min / 60min=13hrs)
Example: TIMEDST 1,1,10,1,2,660
_If time zone is NOT 99, DST is not used (even if displayed) see
Timezone -13..+13 = set time zone offset from UTC in hours
-13:00..+13:00 = set time zone offset from UTC in hours and minutes
99 = use time zone configured with TimeDst and TimeStd
Use the Tasmota time zone command helper to find the commands for your time zone and correctly set sunrise and sunset times.
Ufs Universal File System commands read more...
UfsDelete Delete SD card or Flash FS file if only of them available
UfsDelete2 Delete only Flash FS file if available
UfsFree Filesystem free size in kb
UfsRename Rename SD card or Flash FS file if only of them available
UfsRename2 Rename only Flash FS file if available
Example: UfsRename2 old,new
UfsRun Run file
UfsSize Filesystem size in kb
UfsType Get filesystem type
0 = none
1 = SD card
2 = Flash file
3 = LittleFS
Upgrade 1 = download firmware from OtaUrl and restart
2 = (ESP32 only) download safeboot firmware based on OtaUrl and restart into safeboot
<value> = download firmware from OtaUrl if <value> is higher than device version
Upload 1 = download firmware from OtaUrl and restart
2 = (ESP32 only) download safeboot firmware based on OtaUrl and restart into safeboot
<value> = download firmware from OtaUrl if <value> is higher than device version
UrlFetch <address> = download file to filesystem
WebGetConfig <url> = pull a configuration .dmp file from a HTTP URL
More information...
WebLog 0 = disable web logging
1 = show only error messages
2 = show error and info messages (default)
3 = show error, info and debug messages
4 = show error, info and more debug messages
WebTime <start_pos>,<end_pos> = show part of date and/or time in WebUI based on "2017-03-07T11:08:02-07:00"
See also SetOption68 - PWM Channel control
SetOption76 - DeepSleep disable bootcount incrementing

Wi-Fi~

Command Parameters
Ap 0 = switch to other Wi-Fi Access Point
1= select Wi-Fi Access Point 1
2= select Wi-Fi Access Point 2
CORS " = disable CORS (Cross Origin Resource Sharing) (default)
* = enable CORS for all locations
value = Enable CORS for location. This needs to be complete url ex: http://tasui.shantur.com
Hostname 1 = reset hostname to MQTT_TOPIC-<4digits> and restart
<value> = set hostname (32 char limit) and restart. If hostname contains % it will be reset to the default instead. See FAQ for allowed characters.
If using MQTT to issue this command, if it is used with the device GroupTopic, the command will not be executed.
IPAddress<x> Set networking IP (XXX.XXX.XXX.XXX) addresses
IPAddress1 to set device IP address
  • 0.0.0.0 to use dynamic IP address (DHCP)
  • XXX.XXX.XXX.XXX to set static IP address
  • IPAddress2 to set gateway IP address
    IPAddress3 to set subnet mask
    IPAddress4 to set DNS server IP address
    IPAddress5 to set Secondary DNS server IP address

    follow IPAddress commands with restart 1 to apply changes
    Password<x> <x> = 1..2
    <value> = set AP<x> Wi-Fi password and restart
    1 = reset AP<x> Wi-Fi password to firmware default (STA_PASS1 or STA_PASS2) and restart
    Passwords are limited to 64 characters. Do not use special characters or white spaces in the password.
    Note that Password and Password1 are equivalent commands.
    Ping<x> <addr> <x> = 0..8 = the number of ICMP packets to send, 0 uses the default (4)
    <addr> = address to send Ping, either in numerical format 192.168.1.200 or domain name tasmota.com

    (requires #define USE_PING)
    Example Ping4 192.168.1.203: RSL: tele/tasmota_xxx/RESULT = {"Ping":{"192.168.1.203":{"Reachable":true,"Success":4,"Timeout":0,"MinTime":59,"MaxTime":167,"AvgTime":116}}}
    SSId<x> <x> = 1..2
    <value> = set AP<x> Wi-Fi SSID and restart
    1 = reset AP<x> Wi-Fi SSID to firmware default (STA_SSID1 or STA_SSID2) and restart
    SSID are limited to 32 characters. Do not use special characters or white spaces in the SSID
    TCPBaudrate Requires GPIOs TCP Tx and TCP Rx and can work with hardware or software serial.
    1200..115200 = set the baudrate for serial (only 8N1 mode)
    TCPConnect ,
    <port> = Port used for connection
    = IP address to connect to
    TCPConfig <value> = standard 3 characters mode such as 8N1, 7E1, etc ...
    TCPStart Requires GPIOs TCP Tx and TCP Rx and can work with hardware or software serial. Also works with ModBus Bridge
    <port>, [<ipaddress>] = Start listening to port. If <ipaddress> is defined only allows connections from the provided IPv4 address
    0 = Shut down TCP server and disconnect any existing connection
    Supports 2 parallel TCP connections, which can be useful if you need a terminal + a specific protocol (like XMODEM). The 3rd connection will disconnect a previous connection. The number of parallel connections is a compile-time option.
    UrlFetch <url>
    Download a url (http or https) and store the content in the filesystem
    ESP32 only
    WebColor<x> Configure Web GUI colors (x = 1..19)
    #RRGGBB = Set color for WebColor<x>
    1 = Global text (Black)
    2 = Global background (White)
    3 = Form background (Greyish)
    4 = Input text (Black)
    5 = Input background (White)
    6 = Console text (Black)
    7 = Console background (White)
    8 = Warning text (Red)
    9 = Success text (Green)
    10 = Button text (White)
    11 = Button (Blueish)
    12 = Button hovered over (Darker blue-ish)
    13 = Restart/Reset/Delete button (Red-ish)
    14 = Restart/Reset/Delete button hover (Darker red-ish)
    15 = Save button (Green-ish)
    16 = Save button hover (Darker greenish)
    17 = Config timer tab text (White)
    18 = Config timer tab background (Light grey)
    19 = Module title and FriendlyName text (Whiteish)
    User themes
    WebPassword Show current web server password
    0 = disable use of password for web UI
    1 = reset password to firmware default (WEB_PASSWORD)
    <value> = set web UI password (32 char limit) for user WEB_USERNAME (Default WEB_USERNAME = admin)
    WebQuery Send HTTP GET, POST, PUT, and PATCH Requests
    <url> <method> [<header1Name:header1Value|header2Name:header2Value...>]<body>
    <url> = HTTP URL to query
    <method> = HTTP Request method. Must be GET, POST, PUT, or PATCH
    [<header1Name:header1Value|header2Name:header2Value...>] (optional) = HTTP Request Headers.
    <body> (optional) = HTTP Request Body. Ignored for GET requests

    Examples
    WebQuery http://www.mysite.com/api/status GET: Simple HTTP GET Request
    WebQuery http://www.mysite.com/api/update POST [Authorization:Bearer xyz|Content-Type:application/json]{"message":"body"}: Sends POST data with an authorization header and Content-Type
    WebQuery http://www.mysite.com/api/set PUT {"message":"body"}: Sends PUT request with a body, but no headers
    WebRefresh Web page refresh
    1000..10000 = set refresh time in milliseconds (default = 2345)
    WebSend Send a command to Tasmota host over http. If a command starts with a / it will be used as a link.
    [<host>:<port>,<user>:<password>] <command>
    <host> = hostname or IP address.
    <port> = port for the device if not the default 80
    <user> = enter username of the device you're sending the command to
    <password> = enter password of the device you're sending the command to
    <command> = command and payload
    example 1: [<ip>] POWER1 ON sends http://<ip>/cm?cmnd=POWER1 ON
    example 2: WebSend [myserver.com] /fancy/data.php?log=1234 sends http://myserver.com/fancy/data.php?log=1234
    WebGetConfig <url>
    Download a configuration (*.dmp) from an http URL. The URL can include %id% which will be substituted by the device's MAC address without the dots. A possible usage for ones that compile their own binary is to include the command in USER_BACKLOG for automatic reconfiguration after a reset 1command.
    WebSensor<x> Control display of sensor telemetry in the web UI
    0 = Do not display sensor's telemetry
    1 = Display sensor's telemetry (default)
    <x> = number corresponding to the sensor - listed in the sns section of the supported sensor spreadsheet
    <x> = 3 Energy telemetry
    Issue a Status 4 to obtain a list of sensor types enabled in the firmware loaded on the device.
    Webserver 0 = stop web server
    1 = start web server in user mode
    2 = start web server in admin mode
    Wifi 0 = disable Wi-Fi
    1 = enable Wi-Fi (default)
    ESP8266 only:
    2 = Wi-Fi mode 802.11b
    3 = Wi-Fi mode 802.11b/g
    4 = Wi-Fi mode 802.11b/g/n
    When wifi is Off it is always returned On after a restart except for a wake-up from deepsleep.
    WifiConfig 0 = disable Wi-Fi Manager and reboot (used with alternate AP)
    2 = set Wi-Fi Manager as the current configuration tool and start Wi-Fi Manager (web server at 192.168.4.1) for 3 minutes, then reboot and try to connect Wi-Fi network
    4 = retry other AP without rebooting (default)
    5 = wait until selected AP is available again without rebooting
    6 = Wi-Fi parameters can only be entered via commands in the serial console
    7 = set Wi-Fi Manager (web server at 192.168.4.1) as the current configuration tool restricted to reset settings only.
    This setting is recommended for devices without an external control/reset button.
    No longer supported
    1 = set SmartConfig (Android/iOS) for 3 minutes
    3 = set WPS for 3 minutes
    WifiPower set Wi-Fi transmit power level in decibel-milliwatts (dBm) (default = 17)
    0 = enable dynamic Wi-Fi power based on RSSI
    1 = restore default Wi-Fi power
    WiFiScan 1 = start a network scan. Results will be sent as a JSON payload. Read more...
    WiFiTest<x> Only available in AP mode. Test whether the Wi-Fi SSId and Password are correct and Tasmota can connect to the network. <x> = 0..3
      0 = test credentials, if successful save them in SSID slot 1, restart Tasmota
      1 = test credentials, if successful save them in SSID slot 1 without restart
      2 = test credentials, if successful save them in SSID slot 2 without restart
      3 = test credentials without storing anything persistently and without restart
    ssid+password = credentials used for testing, + symbol is the separator since it is not allowed in an SSId name.* Read more...
    See also SetOption55 - mDNS service control
    SetOption56 - Wi-Fi network scan to select strongest signal on restart
    SetOption57 - Wi-Fi network re-scan, alternate AP

    MQTT~

    Command Parameters
    ButtonRetain 0 = disable use of MQTT retain flag (default)
    1 = enable MQTT retain flag on button press
    ButtonTopic <value> = set MQTT button topic
    0 = disable use of MQTT button topic
    1 = set MQTT button topic to device %topic%
    2 = reset MQTT button topic to firmware default (MQTT_BUTTON_TOPIC) (default = 0)
    If using MQTT to issue this command, if it is published to the device GroupTopic, the command will not be executed.
    FullTopic 1 = reset MQTT fulltopic to firmware default (MQTT_FULLTOPIC) and restart
    <value> = set MQTT fulltopic and restart. Use of optional %prefix%, %topic%, %hostname%, and %id% substitution tokens is allowed.
    If using MQTT to issue this command, if it is published to the device GroupTopic, you must ensure uniqueness of the resulting fulltopic on each destination device by using one or more of these substitution tokens.
    GroupTopic<x> 1 = reset MQTT group <x> topic to firmware default (MQTT_GRPTOPIC) and restart
    <value> = set MQTT group <x> topic and restart
    InfoRetain 0 = disable use of info MQTT retain flag (default)
    1 = enable MQTT retain flag on message tele/%topic%/INFO<x>
    MqttClient 1 = reset MQTT client to firmware config (MQTT_CLIENT_ID) and restart
    <value> = set MQTT client and restart.
    You can use the %06X substitution token to replace with last six characters of MAC address.
    If using MQTT to issue this command, if it is used with the device GroupTopic, the command will not be executed.
    MqttFingerprint TLS needs to be enabled in firmware for this command
    <value> = set current fingerprint as 20 space separated bytes (59 chars max)
    MqttHost 0 = clear MQTT host field and allow mDNS to find MQTT host
    1 = reset MQTT host to firmware default (MQTT_HOST) and restart
    <value> = set MQTT host and restart (do NOT use .local)
    MqttKeepAlive 1..100 = set MQTT Keep Alive timer (default = 30)
    MqttPassword 0 = clear MQTT password
    1 = reset MQTT password to firmware default (MQTT_PASS) and restart
    <value> = set MQTT password and restart (min 5 chars)
    MqttPort 1 = reset MQTT port to firmware default (MQTT_PORT) and restart
    <value> = set MQTT port between 2 and 32766 and restart
    MqttRetry 10..32000 = set MQTT connection retry timer in seconds (default = 10)
    MqttTimeout 1..100 = set MQTT socket timeout (default = 4)
    MqttUser 0 = clear MQTT user name
    1 = reset MQTT user name to firmware default (MQTT_USER) and restart
    <value> = set MQTT user name and restart
    MqttWifiTimeout 100..20000 = set MQTT Wi-Fi connection timeout in milliseconds (default = 200)
    PowerRetain MQTT power retain state
    0 / off = disable MQTT power retain on status update (default)
    1 / on = enable MQTT power retain on status update
    Prefix1 1 = reset MQTT command subscription prefix to firmware default (SUB_PREFIX) and restart
    <value> = set MQTT command subscription prefix and restart
    Prefix2 1 = reset MQTT status prefix to firmware default (PUB_PREFIX) and restart
    <value> = set MQTT status prefix and restart
    Prefix3 1 = Reset MQTT telemetry prefix to firmware default (PUB_PREFIX2) and restart
    <value> = set MQTT telemetry prefix and restart
    Publish <topic> <payload> = MQTT publish any topic and optional payload 
    Publish2 <topic> <payload> = MQTT publish any topic and optional payload with retain flag
    SensorRetain 0 = disable use of sensor MQTT retain flag (default)
    1 = enable MQTT retain flag on message tele/%topic%/SENSOR
    StateRetain 0 = disable use of state MQTT retain flag (default)
    1 = enable MQTT retain flag on message tele/%topic%/STATE
    StatusRetain 0 = disable use of status MQTT retain flag (default)
    1 = enable MQTT retain flag on message tele/%topic%/STATUS
    StateText<x> <value> = set state text (<x> = 1..4)
    1 = OFF state text
    2 = ON state text
    3 = TOGGLE state text
    4 = HOLD state text
    StatusRetain 0 = disable use of status MQTT retain flag (default)
    1 = enable MQTT retain flag on status messages tele/%topic%/STATUS[n]
    SwitchRetain 0 = disable use of MQTT retain flag (default)
    1 = enable MQTT retain flag on switch press
    Subscribe Subscribes to an MQTT topic without appended /# and assigns an Event name to it.
    <eventName>, <mqttTopic> [, <key>] = Read more...
    = list all topics currently subscribed
    Subscribe2 Subscribes to an MQTT topic and assigns an Event name to it.
    <eventName>, <mqttTopic> [, <key>] = Read more...
    = list all topics currently subscribed
    SwitchTopic <value> = set MQTT switch topic
    0 = disable use of MQTT switch topic
    1 = set MQTT switch topic to device %topic%
    2 = reset MQTT switch topic to firmware default (MQTT_SWITCH_TOPIC) (default = 0)
    Read more about this.
    If using MQTT to issue this command, if it is used with the device GroupTopic, the command will not be executed.
    TelePeriod See current value and force publish STATE and SENSOR message
    0 = disable telemetry messages
    1 = reset telemetry period to firmware default (TELE_PERIOD)
    10..3600 = set telemetry period in seconds (default = 300)
    Topic 1 = reset MQTT topic to firmware default (MQTT_TOPIC) and restart
    <value> = set MQTT topic and ButtonTopic and restart.
    When using MQTT to issue this command, if it is used with the device GroupTopic, the command will not be executed. Topic can not be identical to MqttClient
    Unsubscribe Unsubscribe from topics subscribed to with Subscribe
    = unsubscribe all topics
    <eventName> = unsubscribe from a specific MQTT topic
    See also SetOption3 - Disable//Enable MQTT
    SetOption4 - Return MQTT response as RESULT or %COMMAND% topic
    SetOption10 - Main topic change behavior
    SetOption104 - Disable MQTT retained messages (some brokers don't support them)
    SetOption140 - open clean or persistent MQTT session

    Rules~

    Command Parameters
    Add<x> <value> = add value to Var<x> (example)
    CalcRes Current calculation resolution
    0..7 = set number of decimal places to be used in Add, Sub, Mult and Scale
    Event Execute an event to trigger a rule as documented
    Mem<x> Manage up to 16 variables stored on flash (x = 1..16)
    Mem returns all current values. Mem<x> returns the variable's current value.
    <value> = store a string value in a variable
    " = clear stored value in Mem<x>
    Mult<x> <value> = multiply value to Var<x> (example)
    Rule<x> Rules. Read more...
    0 = disable Rule<x>
    1 = enable Rule<x>
    2 = toggle Rule<x>
    4 = disable one-shot detection (perform commands as long as trigger is met)
    5 = enable one-shot (e.g., sometimes used for slow changing sensors like temperature) detection
    6 = toggle one-shot detection
    8 = disable stop-on-error after exception restart
    9 = enable stop-on-error after exception restart
    10 = toggle stop-on-error after exception restart
    <value> = define Rule<x>
    +<value> = append to Rule<x>
    " = clear Rule<x>

    Rule set one-shot: Each rule within the rule set will trigger only once until the trigger condition returns to a false condition. For example, ON Energy#Power<3: Without one-shot enabled, it will trigger anytime Energy#Power gets an update (i.e., the Power telemetry value changes) and the value is <3. This can potentially trigger that rule multiple times. With one-shot enabled, the rule will trigger only the on the first transition to <3 and not again until the trigger value goes >=3. In other words, the rule will trigger again, but it has to cross the conditional "boundary" before it will trigger again.
    Rule0 Same functionality as Rule<x> but affects all rulesets at once
    RuleTimer<x> Up to eight timers to be used as countdown event (x = 1..8) 
    0..65535 = set countdown rule timer in seconds
    RuleTimer0 0 = stops and clear all timer simultaneously
    Scale<x> Scale value from a low and high limit to another low and high limits and save in Var<x> (example)
    v = value: the number to scale
    fl = fromLow: the lower bound of the value’s current range
    fh = fromHigh: the upper bound of the value’s current range
    tl = toLow: the lower bound of the value’s target range
    th = toHigh: the upper bound of the value’s target range
    Sub<x> <value> = subtract value to Var<x> (example)
    Var<x> Manage up to 16 variables stored in memory (x = 1..16)
    Var returns all current values. Var<x> returns the variable's current value.
    <string> = store a string value in a variable
    " = clear stored value in Var<x>

    Timers~

    Command Parameters
    Latitude <value> = set latitude in decimal degrees format, e.g. -33.893681
    Longitude <value> = set longitude in decimal degrees format, e.g. 18.619954
    Sunrise Sunrise/Sunset type
    0 = Normal (default)
    1 = Civil
    2 = Nautical
    3 = Astronomical
    introduced in version 12.1.1.5
    Timers Timers control
    0 = disable all timers
    1 = enable all timers
    2 = toggle all timers
    Timer<x> Parameters for Timer<x> where x = 1..16
    0 = clear parameters for Timer<x>
    1..16 = copy Timer<y> parameters to Timer<x>
    { "name":value ; .. } = set all or individual parameters using JSON payload with names and values of data pairs from the table

    Information on sensors documented below is transmitted in the Tasmota telemetry message

    Sensors~

    Command Parameters
    AdcParam<x> ADC analog input tuning parameters. On ESP32 x is channel 1..8
    <sensor>, <param1>, <param2>, <param3>, <param4>
    complete <sensor> values listed here...
    Altitude -30000..30000 = altitude in meters
    AmpRes Current sensor resolution
    0..3 = maximum number of decimal places
    Bh1750Resolution<x> BH1750 resolution mode. x = BH1750 sensor number (1..2)
    0..2 = choose sensor resolution (0 = high (default), 1 = high2, 2 = low)
    Bh1750MTime<x> BH1750 Measurement Time value. x = BH1750 sensor number (1..2)
    30..255 = set Measurement Time value. Not persistent after reboot. (default = 69)
    Counter<x> 0 = reset Counter<x>
    1..2,147,483,645 = preset Counter<x>
    -1..-2,147,483,645 = decrease Counter<x>
    +1..+2,147,483,645 = increase Counter<x>
    In order to define and use a Counter, *you must configure one of the free device GPIO as Counter<x>. Counter module configuration is using internal pull-up resistor while Countern does not. *
    CounterDebounce 0 = turn off counter debounce
    1..32000 = set counter debounce time in milliseconds. Counter is increased with every falling edge when CounterType=0 or time between successive falling edges is measured when CounterType=1. When CounterDebounceLow and CounterDebounceHigh are set to zero (default) only falling edges of the counter's GPIO are checked. Any CounterDebounceLow or CounterDebounceHigh unequal zero checks are carried out before CounterDebounce check is done. As an example you can set CounterDebounce 500 to allow a minimum distance between to successive valid falling edges equal to 500ms.
    CounterDebounceLow 0 = turn off counter debounce low
    1..32000 = set counter debounce low time in milliseconds. Allow individual debounce times for low pulse widths to discard non valid falling edges. These are checked before legacy CounterDebounce checks distance between two valid falling edges. When unequal zero tasmota will check falling and rising edges on the counter's GPIO. For CounterDebounceLow any GPIO change from low to high hat happens after the GPIO was not low for at least CounterDebounceLow will be ignored. As an example you can set CounterDebounceLow 50 to allow a valid minimum distance between a falling and rising edge equal to 50ms while having a final CounterDebounce 500 check between to successive valid falling edges equal to 500ms.
    CounterDebounceHigh 0 = turn off counter debounce high
    1..32000 = set counter debounce high time in milliseconds. Allow individual debounce times for high pulse widths to discard non valid falling edges. These are checked before legacy CounterDebounce checks distance between two valid falling edges. When unequal zero tasmota will check falling and rising edges on the counter's GPIO. For CounterDebounceHigh any GPIO change from high to low hat happens after the GPIO was not high for at least CounterDebounceHigh will be ignored. As an example you can set CounterDebounceHigh 100 to allow a valid minimum distance between a rising and falling edge equal to 100ms while having a final CounterDebounce 500 check between to successive valid falling edges equal to 500ms.
    CounterType<x> 0 = set Counter<x> as pulse Counter
    1 = set Counter<x> as pulse Timer
    DhtDelay<x> <low_delay>,<high_delay> = set delay for sensor <x>
    1 = reset sensor <x> to default
    Driver15<x> [TBC] for PCA9685
    Driver44<x> [TBC] for Wemos motors <command>,<motor>,<direction>{,<duty>}
    Driver64<x> [TBC] for PCA9632
    DS18Alias List DS18x20 sensors with full IDs
    <sensor ID>,<n> = set sensor alias (number)
    <sensor ID>,0 = remove alias
    GlobalHum 0.0..100.0 = Set global Humidity for some Sensors that uses global Humidity.
    GlobalHum2 1..250 = select Global Humidity source indexed from teleperiod occurrence data.
    GlobalPress2 1..250 = select Global Pressure source indexed from teleperiod occurrence data.
    GlobalTemp -50.0..100.0 = Set global Temperature for some Sensors that uses global temperature.
    GlobalTemp2 1..250 = select Global Temperature source indexed from teleperiod occurrence data.
    HumOffset -10.0..10.0 = Set calibration offset value for reported humidity telemetry
    This setting affects all humidity sensors on the device.
    HumRes Humidity sensor resolution
    0..3 = maximum number of decimal places
    PressRes Pressure sensor resolution
    0..3 = maximum number of decimal places
    Sensor12 ADS1115 mode selection (default S0). Note that Vdd (2.0-5.5v) must be >= analog voltage inputs.
    D0 .. D5 = differential modes
    S0 .. S5 = single-ended modes
    0 = +/- 6.144v
    1 = +/- 4.096v
    2 = +/- 2.048v
    3 = +/- 1.024v
    4 = +/- 0.512v
    5 = +/- 0.256v
    Sensor13 INA219 and ISL28022 low voltage current sensor configuration
    There are 3 ways of configuring the INA219s
    1) Standard 0.1 ohm resistor (same setting applies to all INA219s):
    0,1,2 = set INA219 calibration to max 32V and 2A
    In all cases, ISL28022 is set to 60V mode

    2) Custom shunt resistor (legacy, same setting applies to all INA219)
    10..255: Define custom shunt resistor encoded as a decimal number RRM such that Rshunt = RR * 10^M milliohm
    Do not forget to choose a resistor adapted for the correct power dissipation and apply a 50% security margin !
    Examples:
    11 = 1 * 10^1 = 10 milliohm (Imax=32A => Pres=15W)
    21 = 2 * 10^1 = 20 milliohm (Imax=16A => Pres=7W)
    12 = 1 * 10^2 = 100 milliohm (default, Imax=3.2A => Pres=2W)
    13 = 1 * 10^3 = 1000 milliohm = 1 ohm (Imax=0.320A => Pres=0,2W)

    3) Fully flexible custom shunt (any value, independant setting per INA219)
    0, rshunt1, rhunt2, rshunt3, rshunt4 = set each INA219 with it's own shunt resistor in Ohm (floating point). Exemple:
    Sensor13 0, 0.1, 0.0005, 1.0, 0.0001 configure 1st INA219 with 0.1 Ohm, 2nd with 0.5 mOhm, 3rd with 1 Ohm, 4th with 0.1 mOhm
    IMPORTANT: This configuration mode is NOT saved to flash and requires a boot-rule to initialize the driver at each boot:
    Rule1 on system#init do Sensor13 Sensor13 0, 0.1, 0.0005, 1.0, 0.0001 endon

    Note: The driver seamlessly detect INA219/ISL28022 and adapt configuration and readings accordingly. The component label in Web GUI and SENSOR message will automatically match the detected part.
    It is possible to mix INA219 and ISL28022 as far as addresses do not conflicts.
    Shunt resistor setting applies to all INA219/ISL28022.
    Sensor15 Automatic Baseline Correction for MH-Z19B CO2 sensor
    0 = disable
    1 = enable (default)
    2 = start manual calibration from 400 ppm of CO2
    9 = reset sensor to factory defaults
    1000 = sets measurement range to 1000ppm CO2
    2000 = sets measurement range to 2000ppm CO2
    3000 = sets measurement range to 3000ppm CO2
    5000 = sets measurement range to 5000ppm CO2
    10000 = sets measurement range to 10000ppm CO2
    Sensor18 PMSx003 particle dust sensor control polling interval to extend lifetime
    0..59 = active mode (continuous sensor readings)
    60..65535 - passive mode (read sensor every x seconds)
    Sensor20 Nova Fitness SDS011 dust sensor.
    1..255 = number of seconds before TelePeriod to poll the sensor
    Sensor27 APDS-9960 sensor commands
    0 = enable light level and proximity sensor / disable gestures (default)
    1 = enable gesture mode/ disable light level and proximity sensor
    2 = enable gestures with half gain / disable light and proximity sensor
    3..255 = Set ATIME register for different integration times
    Sensor29 MCP23008 / MCP23017 I2C GPIO Expander configuration. Read more...
    Reset<x> = reset all pins
    x = 1..6
    1 = INPUT mode, no reporting, no pull-up
    2 = INPUT mode, report on CHANGE, pull-up enabled
    3 = INPUT mode, report on LOW, pull-up enabled
    4 = INPUT mode, report on HIGH, pull-up enabled
    5 = OUTPUT mode (if enabled by #define USE_MCP230xx_OUTPUT)
    6 = inverted OUTPUT mode (if enabled by #define USE_MCP230xx_OUTPUT)

    pin,pinmode{,intpullup|outstate{,repmode}}
    Continue reading...
    Sensor34
    HX711 load cell sensor calibration. Note that without setting WeightRes the default resolution is to the nearest 1kg. Set WeightRes 3 to read fractions of a kilogram. This applies to the JSON payload too.
    1 = reset display to 0
    2 = start calibration
    2 <value> = set reference weight in grams and start calibration
    3 = show reference weight in grams
    3 <value> = set reference weight in grams
    4 = show calibrated scale value
    4 <value> = set calibrated scale value
    5 = show max weight in gram
    5 <value> = set max weight in grams
    6 = show single item weight in grams
    6 <value> = set single item weight in grams. Once the item weight is set, when items are added to the scale, the telemetry message will report Count as the number of items on the scale
    7 = save current weight to be used as start weight on restart (deprecated since v11.0.0.7)
    8 0/1
    0 = disable JSON message on weight change over 4 grams
    1 = enable JSON message on weight change (see below)
    9 <value> = set minimum delta to trigger JSON message (see above).
    0 = 4 grams (old default)
    1..100 = set delta to 0-99 grams
    101-255 = set delta to 110-1650 grams (10g increments)
    Sensor36 See MGC3130
    Sensor40 See PN532
    Sensor44 See SPS30
    Sensor50 PAJ7620 gesture sensor
    0 = sensor muted, no readings in Tasmota
    1= gesture mode
    2 = proximity mode
    3 = corner mode
    4 = PIN mode
    5 = cursor mode
    Sensor52 iBeacon driver with HM10 or HM17/HM16
    1 and 2 = required only once to initialize the module
    u<x> = sets update interval in seconds (scan tags every <x> seconds) (default = 10)
    t<x> = set timeout interval in seconds (send RSSI=0 if tag is not detected after <x> seconds) (default = 30)
    d1 = enable debug mode (shows all serial traffic in console)
    d0 = disable debug mode_(default = 30)_
    c = clears iBeacon list
    s AT+<command> = send native AT commands
    Sensor53 Smart Meter Interface
    r = reset the driver with a new descriptor specified with the Tasmota Scripting language.
    c<x> <value> = preset counter (x = 1..5) to value when the driver is set to counter mode
    d<x> = disable data decoding and dump meter (x = 1..5) data to the Console. This is used to decipher the meter's data format to define the variable encoding in the meter's descriptor.
    d0 = disable data dump mode and revert to decoding mode.
    l<x> = monitor the serial activity at a GPIO with a connected LED. x = GPIO of the LED.
    l255 = disable monitoring (default)
    m<x> = serial meter number (x = 1..5) to be monitored
    m0 = monitor all serial meters (default)
    Sensor54 INA226 Current Sensor
    1 = rescan for devices and return the number found.
    2 = save the configuration and restart
    10 = return channel 1 shunt resistance, full scale current and full scale voltage
    11 <resistance> = set INA226 channel 1 shunt in ohms, floating point
    12 <current> = set INA226 channel 1 full scale in amperes, floating point
    13 <voltage> = set INA226 channel 1 full scale in volts, floating point (defaults to 40.96v, not persistent, must be set at boot)
    20 = return channel 2 shunt resistance and full scale current
    21 <resistance> = set INA226 channel 2 shunt in ohms, floating point
    22 <current> = set INA226 channel 2 full scale in amperes, floating point
    23 <voltage> = set INA226 channel 2 full scale in volts, floating point (defaults to 40.96v, not persistent, must be set at boot)
    30 = return channel 3 shunt resistance and full scale current
    31 <resistance> = set INA226 channel 1 shunt in ohms, floating point
    32 <current> = set INA226 channel 3 full scale in amperes, floating point
    33 <voltage> = set INA226 channel 3 full scale in volts, floating point (defaults to 40.96v, not persistent, must be set at boot)
    40 = return channel 4 shunt resistance and full scale current
    41 <resistance> = set INA226 channel 1 shunt in ohms, floating point
    42 <current> = set INA226 channel 4 full scale in amperes, floating point
    43 <voltage> = set INA226 channel 4 full scale in volts, floating point (defaults to 40.96v, not persistent, must be set at boot)
    Sensor60 GPS
    0 = write to all available sectors, then restart and overwrite the older ones
    1 = write to all available sectors, then restart and overwrite the older ones
    2 = filter out horizontal drift noise
    3 = turn off noise filter
    4 = start recording, new data will be appended
    5 = start new recording, old data will lost
    6 = stop recording, download link will be visible in webUI
    7 = send mqtt on new position + TELE (consider to set TELE to a very high value)
    8 = only TELE message
    9 = start NTP server
    10 = deactivate NTP server
    11 = force update of Tasmota-system-UTC with every new GPS-time-message
    12 = do not update of Tasmota-system-UTC with every new GPS-time-message
    13 = set latitude and longitude in settings
    14 = open virtual serial port over TCP, usable for u-center
    15 = pause virtual serial port over TCP
    Sensor68 WindMeter sensor - Analog (pulse count) anemometer
    1, <value> = set radius length in millimeters (measured from center to the edge of one of the cups) 0..65535 (default = 61mm)
    2, <value> = set number of pulses for a complete turn 1..255 (default = 1)
    3, <value> = set pulse counter debounce time in milliseconds 1..32000 (default = 10)
    4, <value> = set speed compensation factor, a multiplication coefficient to adjust resulting speed -32.768..32.767 three decimal places (default = 1.180)
    5, <value> = set minimum percentage change between current and last reported speed trigger a new tele message 0..100, 255 = off (default = 255)
    Sensor78 EZO sensors - commands
    Ascii commands are sent directly to the sensor as-is. See your specific EZO device datasheet for the list of commands available.
    By default, the specific command is sent to all EZO devices that are found. If using multiple EZO sensors, and the command should be issued to a single device, the index can be specified as part of the command: Sensor78-# where # represent the index of the device (ex: Sensor78-1 i). For more details please see Tasmota's support for EZO devices.
    Sensor80 Set antenna gain for MFRC522 RFID Reader.
    Sensor80 1 <0..7>
    0 18dB
    1 23dB
    2 18dB
    3 23dB
    4 33dB
    5 38dB
    6 43dB
    7 48dB
    Sensor90 Send commands to Hydreon RG-15 Rain Sensor
    A Reads accumulation data
    R Read all available data
    K Restart the rain sensor
    P Set to polling only mode (not supported)
    C Set to continuous mode, where data is sent when accumulation changes (default)
    H Force high resolution
    L Force low resolution
    I Force imperial (not supported)
    M Force metric (default)
    S Revert to jumper configured values
    O Reset the accumulation counter
    Sensor95 CM110x automatic baseline correction, 1=enable (default), 0=disable
    Sensor96 USE_FLOWRATEMETER settings
    SpeedUnit TX20/TX23 and WindMeter anemometer speed unit
    1 = m/s
    2= km/h
    3 = kn
    4 = mph
    5 = ft/s
    6 = yd/s
    TempRes Temperature sensor resolution
    0..3 = maximum number of decimal places
    TempOffset -12.6..12.6 = Set calibration offset value for reported temperature telemetry
    This setting affects all temperature sensors on the device.
    VoltRes Voltage sensor resolution
    0..3 = maximum number of decimal places
    WattRes Power sensor resolution
    0..3 = maximum number of decimal places
    WeightRes Load cell sensor resolution
    0..3 = maximum number of decimal places (the default is 0)
    Wiper DS3502 contains a single potentiometer whose wiper position is controlled by the value in the Wiper Register (WR) represented by x = 0..3
    POTI 0..127 = set POTI for wiper x
    STATUS = get wiper position for wiper x
    RESET = reset settings for wiper x
    See also SetOption8 - Show temperature in Celsius (default) or Fahrenheit
    SetOption18 - Set status of signal light paired with CO2 sensor
    SetOption24 - Set pressure units

    Power Monitoring~

    Command Parameters
    AmpRes Current sensor resolution
    0..3 = maximum number of decimal places
    BatteryPercentage 0..101 (default = 101= disabled)
    Set the current battery level as a percentage reported by STATE and STATUS11. This can be used to monitor e.g. in `Home Automation" the battery lifetime.
    The intended use-case is to generate a rule that calculates the percentage based on e.g. A0 value or something else.
    CurrentCal 1000..32000 (default = 3500)
    Set calibration offset ry
    Allows finer calibration for energy monitoring devices
    CurrentHigh 0 = disable current high threshold (default)
    <value> = set current high threshold value in milliamps
    CurrentLow 0 = disable current low threshold (default)
    <value> = set current low threshold value in milliamps
    CurrentSet<x> <value> = calibrate current to target value in mA
    x = 1 or 2 indicating phase
    EnergyExport<x> Export energy values
    <x> = meter number (default is 1)
    EnergyExportActive<x> ADE7880 only! Set/reset energy active values
    <x> = meter number (default is 1)
    EnergyReset<x> x = 1..5
    1 <value>{,<time>} = ((p)re)set values
    2 <value>{,<time>} = ((p)re)set values for Yesterday
    3 <value>{,<time>} = ((p)re)set values for Total
    <value> = 0..42949672 in watt-hours (Wh)
    <time> = 0..4294967295 set StartTotalTime time as epoch value
    4 <standard>{,<off-peak>} = ((p)re)set tariff period values for Totals
    5 <standard>{,<off-peak>} = ((p)re)set tariff period values for Exported
    Warning: After v10, this command is removed. As a replacement use EnergyToday 0, EnergyTotal 0 and EnergyYesterday 0
    EnergyRes Energy sensor resolution
    0..5 = maximum number of decimal places
    EnergyToday<x> Get or set Energy Today values, parameters:
    <x> = meter number (default is 1)
    <value> = set new value in Wh, 0 for reset
    <time> = 0..4294967295 set StartTotalTime time as epoch value (optional 2nd parameter)
    EnergyTotal<x> Get or set Energy Total values, parameters:
    <x> = meter number (default is 1)
    <value> = set new value in Wh, 0 for reset
    <time> = 0..4294967295 set StartTotalTime time as epoch value (optional 2nd parameter)
    The new value represents start of day, and output for total includes the today value.
    EnergyUsage Set energy usage values, parameters:
    <value> = set energy usage value, 0 for reset
    EnergyYesterday<x> Get or set Energy Yesterday values, parameters:
    <x> = meter number (default is 1)
    <value> = set new value in Wh, 0 for reset
    <time> = 0..4294967295 set StartTotalTime time as epoch value (optional 2nd parameter)
    FreqRes Frequency sensor resolution
    0..3 = maximum number of decimal places
    FrequencySet <value> = calibrate frequency to a target value in Hz
    MaxPower 0 = disable use maximum power monitoring
    <value> = set maximum allowed power in watts
    MaxPowerHold 1 = set default time to 10 seconds to stay over MaxPower before power off
    <value> = set time in seconds to stay over MaxPower before power off
    MaxPowerWindow 1 = set default time to 30 seconds to stay power off before re-applying power up to 5 times
    <value> = set time in seconds to stay power off before re-applying power up to 5 times
    ModuleAddress Set the address of a PZEM module
    1..3 = the last octet of the PZEM-004T serial address
    <address> = the last octet of the address on MODBUS PZEM energy monitoring modules
    Prior to setting the module address, the PZEM must be connected to both RX and TX, and AC voltage.
    Connect one PZEM at a time and issue this command. Repeat for each PZEM to be connected for multi-phase monitoring.
    The command without an argument cannot be used to read the address of the connected PZEM.
    PowerCal 1000..32000 (default = 12530)
    Set calibration offset value for reported Power telemetry reading
    Allows finer calibration for energy monitoring devices
    PowerDelta<x> Set maximum delta of phase a in energy monitoring devices to report on active power load change while the power is ON. PowerDelta will not report when the power turns off. 
    0 = disable reporting on power change
    1..100 = set reporting on percentage power change to send an MQTT telemetry message
    101..32000 = set reporting on absolute power change to send an MQTT telemetry message (offset by 100, e.g., 101=1W, 207=107W)
    PowerHigh 0 = disable power high threshold (default)
    <value> = set power high threshold value in watts to send an MQTT telemetry message
    PowerLow 0 = disable power low threshold (default)
    <value> = set power low threshold value in watts to send an MQTT telemetry message
    PowerSet<x> <value> = calibrate power to a target value in watts
    x = 1 or 2 indicating phase
    Status 8 = show power usage
    9 = show power thresholds
    Tariff<x> P1 Smart Meter tariff configuration
    x = 1, 2, 9
    1 STD,DST Start times for off-peak tariff
    2 STD,DST End times for off-peak tariff
    9 0/1
    0 = use Start/End times also on weekends.
    1 = use off-peak tariff all weekend.
    STD and DST may be specified as:
    <hour> = 0..23 or
    <time> = 00:00..23:59 or
    <minutes> = 0..1439 (since midnight)
    If both Tariff1 STD and Tariff2 STD are equal, all tariffs are disabled.
    VoltageCal Set calibration offset value for reported Voltage telemetry reading
    1000..32000 (default = 1950)
    Allows finer calibration for energy monitoring devices
    VoltageHigh 0 = disable voltage high threshold (default)
    <value> = set voltage high threshold value in V
    VoltageLow 0 = disable voltage low threshold (default)
    <value> = set voltage low threshold value in V
    VoltageSet<x> <value> = calibrate voltage to a target value in V
    x = 1 or 2 indicating phase
    VoltRes Voltage sensor resolution
    0..3 = maximum number of decimal places
    WattRes Power sensor resolution
    0..3 = maximum number of decimal places
    See Also SetOption21 - Energy monitoring when power is off
    SetOption33 - Configure power monitoring Max_Power_Retry count number
    SetOption39 - Control handling of invalid power measurements
    SetOption72 - Set reference used for total energy
    SetOption129 - Enable separate energy meters

    Light~

    Command Parameters
    AlexaCTRange
    SetOption82
    Reduce the CT range from 153..500 to 200..380 to accommodate with Alexa range
    0 = CT ranges from 153 to 500 (default)
    1 = CT ranges from 200 to 380 (although you can still set in from 153 to 500)
    Channel<x> 0..100 = set PWM channel dimmer value from 0 to 100% 
    + = increase by 10
    - = decrease by 10
    When SetOption68 is set to 1 Channel<x> will follow Power<x> numbering with Relays first then PWM.
    Example:
    2 Relays and 3 PWM: Relay1 = Power1; Relay2 = Power2; PWM1 = Power3 and Channel3; PWM2 = Power4 and Channel4; PWM3 = Power5 and Channel5
    ChannelRemap
    SetOption37
    Color remapping for led channels, also provides an option for allowing independent handling of RGB and white channels. Setting changes require a device reboot.
    0 = disable
    1..119 = according to this table
    120..127 = invalid (results in same as 0)
    128..255 = same as 0..127 but with independent channel handling enabled
    Color<x> x = 1..6
    1 = Set color
    2 = Set color adjusted to current Dimmer value
    3 = Set clock seconds hand color (Scheme 5 only)
    4 = Set clock minutes hand color (Scheme 5 only)
    5 = Set clock hour hand color (Scheme 5 only)
    6 = Set clock hour marker color
    <value>
    r,g,b = set color by decimal value (0..255)
    #CWWW = set hex color value for CT lights
    #RRGGBB = set hex color value for RGB lights
    #RRGGBBWW = set hex color value for RGBW lights
    #RRGGBBCWWW = set hex color value for RGBCCT lights (5 PWM channels)
    Note:
    Just append an = instead of the remaining color codes, this way they wont get changed. For example a command like Color #00ff= would update the RGB part to disable red and enable green, but would omit to update blue or any white channel.
    Set color to
    1 = red
    2 = green
    3 = blue
    4 = orange
    5 = light green
    6 = light blue
    7 = amber
    8 = cyan
    9 = purple
    10 = yellow
    11 = pink
    12 = white (using RGB channels)
    + = next color
    - = previous color
    CT 153..500 = set color temperature from 153 (cold) to 500 (warm) for CT lights
    + = increase CT value by 10
    - = decrease CT value by 10
    CTRange Specify CT range of the bulb. The slider will still allow to set CT from 153 to 500, but the rendering will be done within the new range.
    <ct_min>,<ct_max> = set color temperature from 153 (cold) to 500 (warm) for CT lights default = 153,500
    This settings is not persisted in flash
    Dimmer 0..100 = set dimmer value from 0 to 100%
    + = increase by DimmerStep value (default = 10)
    - = decrease by DimmerStep value (default =10)
    Use of these parameters with Fade on enables dimmer level "move down," "move up," and "stop" commands (#11269)
    < = decrease to 1
    > = increase to 100
    ! = stop any dimmer fade in progress at current dimmer level
    Dimmer<x> Commands available only when SetOption37 >= 128 (#6819)
    <value> same as in Dimmer
    Dimmer0 <value> = set dimming for all channels
    Dimmer1 <value> = set dimming for RGB channels
    Dimmer2 <value> = set dimming for white channels
    Dimmer4 <value> = allow retaining brightness ratio between white and color channels when setting dimmer for linked lights
    DimmerRange Change dimming range.
    <dimmerMin>,<dimmerMax> = set the internal dimming range from minimum to maximum value (0..255, 0..255)
    Does not change Dimmer command behavior
    DimmerStep 1..50 - set Dimmer +/- step value. (default =10)
    Fade 0 = do not use fade (default)
    1 = use fade
    See also SetOption91
    HSBColor <hue>,<sat>,<bri> = set color by hue, saturation and brightness
    HSBColor1 0..360 = set hue
    HSBColor2 0..100 = set saturation
    HSBColor3 0..100 = set brightness
    L1MusicSync Only for Sonoff L1 (Lite) and Spider Z LED controllers
    <power,sensitivity,speed>
    power = 0 - off, 1 - on, 2 - toggle
    sensitivity = 1..10 (default: 5)
    speed = 1..100 (default: 50)
    Can be used with only power argument
    Led<x> #RRGGBB = set hex color value where <x> is the pixel number of the LED. A blank-delimited list of colors sets multiple successive pixels.
    (applies only to addressable LEDs)
    LedPwmMode<x> Control status LED light mode (x = 0..4)
    0 = digital on/off mode (default)
    1 = PWM mode
    2 = toggle between modes
    LedPwmOff 0..255 = set LED brightness when OFF
    LedPwmOn 0..255 = set LED brightness when ON
    LedTable 0 = do not use LED gamma correction (default «6.5.0.9)
    1 = use gamma correction (default »6.5.0.9)
    MultiPWM
    SetOption68
    Multi-channel PWM instead of a single light
    0 = Treat PWM as a single light (default)
    1 = Treat PWM as separate channels. In this mode, use Power<x> to turn lights on and off, and Channel<x> to change the value of each channel.
    Color still works to set all channels at once.
    Requires restart after change
    Palette 0 = Clear color palette
    [ ...] = Set list of colors used by Color<1,2> and Scheme<2,3,4> commands with each color separated by a space. The palette setting is not saved to flash. Use a boot-time rule such as ON System#Boot DO Palette xxxxx ENDON to set it back at each restart.
    Pixels 1..512 = set amount of pixels in strip or ring and reset Rotation (applies only to addressable LEDs)
    PowerOnFade
    SetOption91
    Enable Fade at boot and power on. By default fading is not enabled at boot because of stuttering caused by wi-fi connection
    0 = don't Fade at startup (default)
    1 = Fade at startup
    PWMCT
    SetOption92
    Alternative to Module 38: for Cold/Warm white bulbs, enable the second PWM as CT (Color Temp) instead of Warm White, as required for Philips-Xiaomi bulbs.
    0 = normal Cold/Warm PWM (default)
    1 = Brightness/CT PWM
    See PWM CT in Lights
    RGBWWTable Control light intensity of unbalanced PWM channels
    PWM1,PWM2,PWM3,PWM4,PWM5 = channel range with values 0..255 (default =255,255,255,255,255)
    Range adjustment is computed after Gamma correction.
    Rotation <value> = set amount of pixels to rotate (up to Pixels value) (applies only to addressable LEDs)
    Scheme Light effects
    + = next scheme
    - = previous scheme
    0 = single color for LED light (default)
    1 = start wake up sequence (same as Wakeup)
    2 = cycle up through colors using Speed option
    3 = cycle down through colors using Speed option
    4 = random cycle through colors using Speed and Fade
    Use <value>, <startcolor> if you want to set the starting color of selected scheme
    Following schemes are usable only with addressable LEDs, e.g. WS281X, Neopixel
    5 = clock mode (example)
    6 = candlelight pattern
    7 = RGB pattern
    8 = Christmas pattern
    9 = Hanukkah pattern
    10 = Kwanzaa pattern
    11 = rainbow pattern
    12 = fire pattern
    13 = stairs pattern
    14 = clear (used to control from Berry)
    Speed 1..40 = set fade speed from fast 1 to very slow 40
    + = increase speed
    - = decrease speed
    The Speed value represents the time in 0.5s to fade from 0 to 100% (or the reverse). Example: Speed 4 takes 2.0s to fade from full brightness to black, or 0.5s to move from 75% to 100%.
    Speed2 Same as Speed but settings aren't stored.
    ! = can be used to cancel the use of a preceding Speed2 command. Use example...
    StepPixels (Scheme 5 only)
    <value> = define the number of LEDs in each step
    VirtualCT Precisely specify color rendering of the bulb for Color Temperature. Needs SetOption106 1 and works for 3, 4 or 5 channel lights
    {"<minct>":"<color1>","midct":"<color2>","maxct":"<color3"}
    Example: VirtualCT {"200":"FFFFFF0000","400":"000000FF00"}
    The first and last CT values indicate the min and max CT and are equivalent to CTRange. Read more...
    This settings is not persisted in flash
    Wakeup Start wake up sequence from OFF to stored Dimmer value
    0..100 = Start wake up sequence from OFF to provided Dimmer value
    WakeUpDuration 1..3000 = set wake up duration in seconds
    White 1..100 = set white channel brightness in single white channel lights (single W or RGBW lights)
    WhiteBlend
    SetOption105
    White Blend Mode
    0 = disable (default)
    1 = enable
    Width<x> x = 1..4
    1 = 0..4 = LED group width (Scheme 6..12 only)
    2 = 0..32 = seconds hand width (Scheme 5 only)
    3 = 0..32 = minutes hand width (Scheme 5 only)
    4 = 0..32 = hour hand width (Scheme 5 only)
    See also SetOption15, SetOption16, SetOption17, SetOption20, SetOption37, SetOption68 and SetOption107

    ArtNet specific commands:

    Command Description
    ArtNetConfig <json> Example ArtNetConfig {"Rows":5, "Cols":5, "Offset":0, "Alternate":false, "Universe":0}
    There are two modes for ArtNet configuration: simple light or adressable leds.
    In simple Light mode, "cols" is zero. Only "Universe" needs to be specified.
    Example: ArtNetConfig {"Cols":0, "Universe":0}
    In Adressable Light mode, all parameters can be specified.

    Rows: number of rows of display, 1 for light mode or single strip
    Cols: number of columns of the display, or length of the strip, or 0 for single light
    "Offset": number of adressable leds to skip
    Alternate: (true/false) indicates that every other line is reversed (common in large matrix)
    Universe: starting DMX Universe number for the first line (0 based so you may need to substract 1 from software)
    ArtNet Start, On>, or1: Start ArtNet mode, listen to UDP port 6454 and forceSetOption148 1for autorun at restart<BR>Stop,Offor0: Stop ArtNet mode, close UDP port and forceSetOption148 0` to disable ArtNet mode at restart
    See also SetOption148

    Device Groups~

    Command Parameters
    DevGroupName<x> 0 = clear device group <x> name and restart
    <value> = set device group <x> name and restart.
    Prior to 8.2.0.3, GroupTopic was used to specify the device group name.
    DevGroupSend<x> <item>=<value>[ ...] = send an update to device group <x>. The device group name must have been previously set with DevGroupName<x>. Multiple item/value pairs can be specified separated by a space. Spaces in <value> must be escaped with a backslash (\). The message sent is also processed on the local device as if it had been received from the network.

    For items with numeric values, <value> can be specified as @<operator>[<operand>] to send a value after performing an operation on the current value. <operator> can be + (add), - (subtract) or ^ (invert). If <operand> is not specified, it defaults to 0xffffffff for the invert operator and 1 for other operators.

    Examples:
    DevGroupSend 4=90 128=1 - send an update to set the light brightness to 90 and turn relay 1 on.
    DevGroupSend 193=Buzzer\\ 2,3 - send the Buzzer 2,3 command.
    DevGroupSend 6=@+ 4=@-10 - set the next fixed color and decrease the brightness by 10.
    DevGroupSend 128=^ - toggle all the relays.

    2 = Light fade (0 = Off, 1 = On)
    3 = Light speed (1..40)
    4 = Light brightness (0..255)
    5 = Light Scheme
    6 = Light fixed color (0 = white (using CT channels), other values according to Color)
    7 = PWM dimmer low preset (0..255)
    8 = PWM dimmer high preset (0..255)
    9 = PWM dimmer power-on brightness (0..255)
    128 = Relay Power - bitmask with bits set for relays to be powered on. The number of relays can be specified in bits 24 - 31. If the number of relays is not specified, only relay 1 is set
    192 = Event - event name and arguments
    193 = Command - command and arguments
    224 = Light channels - comma separated list of brightness levels (0..255) for channels 1 - 5 (e.g. 255,128,0,0,0 will turn the red channel on at 100% and the green channel on at 50% on an RGB light)
    DevGroupShare <in>,<out> = set incoming and outgoing shared items (default = 0xffffffff,0xffffffff)
    <in> and <out> are bit masks where each mask is the sum of the values for the categories (listed below) to be shared. For example, to receive only power (1), light brightness (2) and light color (16) and send only power (1), enter the command DevGroupShare 19,1.

    1 = Power
    2 = Light brightness
    4 = Light fade/speed
    8 = Light scheme
    16 = Light color
    32 = Dimmer settings (presets)
    64 = Event
    DevGroupStatus<x> Show the status of device group <x> including a list of the currently known members.
    See also SetOption85 - to enable Device Groups

    SetOptions~

    Tip

    Instead of typing SetOption you can use shorter form of SO. so instead of SetOption19 1 you can use SO19 1

    Command Parameters
    SetOption0 Save power state and use after restart (=SaveState)
    0 = disable (see note below)
    1 = enable (default)
    Note: Power state means on/off state of eg. relays or lights. Other parameters like color, color temperature, brightness, dimmer, etc. are still saved when changed. To disable saving other parameters see SaveData.
    SetOption1 Set button multipress mode to
    0 = allow all button actions (default)
    1 = restrict to single to penta press and hold actions (i.e., disable inadvertent reset due to long press)
    SetOption2 Set display of global temperature/humidity/pressure info to JSON sensor message
    0 = disable (default)
    1 = enable
    SetOption3 MQTT
    0 = disable MQTT
    1 = enable MQTT (default)
    SetOption4 Return MQTT response as
    0 = RESULT topic (default)
    1 = %COMMAND% topic
    SetOption8 Show temperature in
    0= Celsius (default)
    1 = Fahrenheit
    SetOption10 When the device MQTT topic changes
    0 = remove retained message on old topic LWT (default)
    1 = send "Offline" to old topic LWT
    SetOption11 Swap button single and double press functionality
    0 = disable (default)
    1 = enable
    SetOption12 Configuration saving to flash option
    0 = allow dynamic flash save slot rotation (default)
    1 = use fixed eeprom flash slot
    SetOption13 Allow immediate action on single button press
    0 = single, multi-press and hold button actions (default)
    1 = only single press action for immediate response (i.e., disable multipress detection). Disable by holding for 4 x button hold time (see SetOption32).
    SetOption15 Set PWM control for LED lights
    0 = basic PWM control
    1 = control with Color or Dimmer commands (default)
    SetOption16 Set addressable LED Clock scheme parameter
    0 = clock-wise mode (default)
    1 = counter-clock-wise mode
    SetOption17 Show Color string as
    0 = hex string (default)
    1 = comma-separated decimal string
    SetOption18 Set status of signal light paired with CO2 sensor
    0 = disable light (default)
    1 = enable light
    The light will be green below CO2_LOW and red above CO2_HIGH (transition yellow/orange between). The default levels are: 800ppm for low and 1200ppm for high but these can be set in user_config_override.h.
    SetOption19 Tasmota discovery protocol used in Home Assistant Tasmota integration
    0 = enable Tasmota discovery (default)
    1 = use deprecated MQTT discovery (only with #define USE_HOME_ASSISTANT, does not exist in release binaries)
    SetOption20 Update of Dimmer/Color/CT without turning power on
    0 = disable (default)
    1 = enable
    SetOption21 Energy monitoring when power is off
    0 = disable (default)
    1 = enable
    SetOption24 Set pressure units
    0 = hPa (default)
    1 = mmHg
    SetOption26 Use indexes even when only one relay is present
    0 = messages use POWER (default)
    1 = messages use POWER1
    SetOption28 RF received data format
    0 = hex (default)
    1 = decimal
    SetOption29 IR received data format
    0 = hex (default)
    1 = decimal
    SetOption30 Enforce Home Assistant auto-discovery as light
    0 = relays are announced as a switch and PWM as a light (default)
    1 = both relays and PWM are announced as light
    SetOption31 Set status LED blinking during Wi-Fi and MQTT connection problems.
    LedPower must be set to 0 for this feature to work
    0 = Enabled (default)
    1 = Disabled
    SetOption32 Number of 0.1 seconds to hold button before sending HOLD action message.
    1..100 to set button hold time (default = 40). This option also affects the time required to perform a firmware defaults reset (10x HOLD action time). There is no firmware reset on using the HOLD action with shutterbuttons.
    SetOption33 Number of times to retry applying power after the power has been switched off because of exceeding the maximum power limit
    1..250 = set number of retries (default = 5)
    SetOption34 0..255 = set Backlog inter-command delay in milliseconds (default = 200)
    SetOption35 0..255 = skip number of received messages in Serial Bridge (default = 0)
    SetOption36 Boot loop defaults restoration control.
    0 = disable boot loop control
    1..200 = set number of boot loops (a restart caused by any exception or watchdog timer within less than BOOT_LOOP_TIME (default 10 seconds) before beginning to restore settings (default = 1). Once this number is reached, subsequent restarts will:
    • 1st restart: disable ESP8285 generic GPIOs interfering with flash SPI
    • 2nd restart: disable rules causing boot loop
    • 3rd restart: disable all rules, disable shutters setoption80 0 (and autoexec.bat)
    • 4th restart: reset user defined GPIOs to disable any attached peripherals
    • 5th restart: reset module to Sonoff Basic (1)
    SetOption38 6..255 = set IRReceive protocol detection sensitivity minimizing UNKNOWN protocols
    SetOption39 Control handling of invalid power measurements. Read more...
    0 = reset to default on next restart
    1..255 = number of invalid power readings before reporting no load (default =128).
    SetOption40 Stop detecting input change on the button GPIO. Solves #5449
    Active only when SetOption1 1 and SetOption13 0. This disables all long press functionality.
    0..250 = button hold time in 0.1 seconds after which button functionality is disabled.(default =1)
    Example: Backlog SetOption1 1; SetOption13 0; SetOption40 10 = discard any button press over 1 second
    SetOption41 0 = Disable ARP
    <x> = Force sending gratuitous ARP (Wi-Fi keep alive) every <x> seconds (default = 60)
    If <x> is below 100 it is the number of seconds, if <x> is above 100, it is the number of minutes after subtracting 100. Ex: 105 is every 5 minutes, while 90 is every 90 seconds.
    SetOption42 0..255 = set over-temperature (Celsius only) threshold resulting in power off on all energy monitoring devices (default = 90)
    SetOption43 0..255 = to control Rotary step. Details #10407
    SetOption44 1..100 = set base tolerance percentage for matching incoming IR messages (default = 25)
    SetOption45 1..250 = change bi-stable latching relay pulse length in milliseconds (default = 40)
    SetOption46 0..255 = power on delay before initializing in value * 10 milliseconds SCD30 only
    SetOption47 3..255 = delay power on relay state in seconds to reduce power surge
    1 = delays until network connected
    2 = delays until MQTT connected
    SetOption51 Enable GPIO9 and GPIO10 component selections in Module Configuration
    🚨 WARNING Do not use on ESP8266 devices! 🚨
    0 = disable (default)
    1 = enable
    SetOption52 Control display of optional time offset from UTC in JSON payloads
    0 = disable (default)
    1 = enable
    SetOption53 Display hostname and IP address in GUI
    0 = disable (default)
    1 = enable
    SetOption54 Apply SetOption20 settings to commands from Tuya device
    0 = disable (default)
    1 = enable
    SetOption55 mDNS service
    0 = disable (default)
    1 = enable
    SetOption56 Wi-Fi network scan to select strongest signal on restart (network has to be visible)
    0 = disable (default)
    1 = enable
    SetOption57 Wi-Fi network re-scan every 44 minutes with alternate to +10dB stronger signal if detected (only visible networks)
    0 = disable
    1 = enable (default)
    SetOption58 IR Raw data in JSON payload
    0 = disable (default)
    1 = enable
    SetOption59 Send tele/%topic%/STATE in addition to stat/%topic%/RESULT for commands: State, Power and any command causing a light to be turned on.
    0 = disable (default)
    1 = enable
    SetOption60 Set sleep mode
    0 = dynamic sleep (default)
    1 = normal sleep
    SetOption61 Force local operation when ButtonTopic or SwitchTopic is set.
    0 = disable (default)
    1 = enable
    SetOption62 Set retain on Button or Switch hold messages
    0 = disable (default)
    1 = don't use retain flag on HOLD messages
    SetOption63 Set relay state feedback scan at restart (#5594, #5663)
    0 = Scan power state at restart (default)
    1 = Disable power state scanning at restart
    SetOption64 Switch between - or _ as sensor name separator
    0 = sensor name index separator is - (hyphen) (default)
    1 = sensor name index separator is _ (underscore)
    Affects DS18X20, DHT, BMP and SHT3X sensor names in tele messages
    SetOption65 Device recovery using fast power cycle detection
    0 = enabled (default)
    1 = disabled
    SetOption66 Set publishing TuyaReceived to MQTT
    0 = disable publishing TuyaReceived over MQTT (default)
    1 = enable publishing TuyaReceived over MQTT
    SetOption69 Deprecated in favor of DimmerRange
    By default Tuya dimmers won't dim below 10% because some don't function very well that way.
    0 = disable Tuya dimmer 10% lower limit
    1 = enable Tuya dimmer 10% lower limit (default)
    SetOption71 Set DDS238 Modbus register for active energy
    0 = set primary register (default)
    1 = set alternate register
    SetOption72 Set reference used for total energy
    0 = use firmware counter (default)
    1 = use energy monitor (e.g., PZEM-0xx, SDM120, SDM630, DDS238, DDSU666) hardware counter
    SetOption73 Detach buttons from relays and send multi-press and hold MQTT messages instead
    0 = disable (default)
    1 = enable
    Example message: {"Button1":{"Action":"SINGLE"}}
    SetOption74 Enable internal pullup for single DS18x20 sensor
    0 = disable (default)
    1 = internal pullup enabled
    SetOption75 Set grouptopic behaviour (#6779)
    0 = GroupTopic using FullTopic replacing %topic% (default)
    1 = GroupTopic is cmnd/%grouptopic%/
    SetOption76 Boot count incrementing when DeepSleep is enabled (#6930)
    0 = disable boot count incrementing (default)
    1 = enable boot count incrementing
    SetOption77 Do not power off if a slider is moved to far left
    0 = disable (default)
    1 = enable
    SetOption78 OTA compatibility check
    0 = enabled (default)
    1 = disabled
    SetOption79 Reset counters at TelePeriod time
    0 = disable (default)
    1 = enable
    SetOption80 Blinds and shutters support
    0 = disable blinds and shutters support (default)
    1 = enable blinds and shutters support
    SetOption81 Set PCF8574 component behavior for all ports
    0 = set as regular state (default)
    1 = set as inverted state
    SetOption82 Reduce the CT range from 153..500 to 200.380 to accommodate with Alexa range
    0 = CT ranges from 153 to 500 (default)
    1 = CT ranges from 200 to 380 (although you can still set in from 153 to 500)
    SetOption83 Uses Zigbee device friendly name instead of 16 bits short addresses as JSON key when reporting values and commands
    0 = JSON key as short address
    1 = JSON key as friendly name
    See ZbName <device>,<name>
    SetOption84 (Experimental) When using AWS IoT, sends a device shadow update (alternative to retained)
    0 = don't update device shadow (default)
    1 = update device shadow
    Note: if the Topic contains '/' they are replaced with '_'
    SetOption85 Device group support
    0 = disable (default)
    1 = enable
    SetOption86 PWM Dimmer only! Turn brightness LED's off 5 seconds after last change
    0 = disable (default)
    1 = enable
    SetOption87 PWM Dimmer only! Turn red LED on when powered off
    0 = disable (default)
    1 = enable
    SetOption88 Make each relay part of a separate device group. Relay 1 updates are sent to/received from device group 1, relay 2 updates are sent to/received from device group 2, etc. For the PWM Dimmer module, make each button be associated with a different device group.
    0 = disable (default)
    1 = enable
    SetOption90 Disable sending MQTT with non-JSON messages
    0 = send all MQTT (default)
    1 = send only MQTT messages with JSON payloads
    SetOption93 Control caching of compressed rules
    0 = Disable memory caching of uncompressed rules
    1 = Keep uncompressed rules in memory to avoid CPU load of uncompressing at each tick (default)
    SetOption94 Select MAX31855 or MAX6675 thermocouple support
    0 = Use MAX31855 protocol (default)
    1 = Use simpler MAX6675 protocol instead of MAX31855
    SetOption97 Set TuyaMCU serial baudrate
    0 = 9600 bps (default)
    1 = 115200 bps
    SetOption98 Provide rotary dimmer rule triggers
    0 = disable (default)
    1 = enable
    SetOption99 Enable zero-cross capable AC dimmer
    0 = no zero-cross AC dimmer connected (default)
    1 = zero-cross AC dimmer attached. Focus on raising edge and sync frequency
    SetOption101 Add Zigbee source endpoint as suffix to attributes
    0 = disable (default)
    1 = enable
    e.g. Power3 instead of Power if sent from endpoint 3.
    SetOption103 Set TLS mode
    0 = disable TLS
    1 = enable TLS
    SetOption104 Disable MQTT retained messages (some brokers don't support them)
    0 = retained messages enabled (default)
    1 = retained messages disabled
    SetOption107 Set virtual CT channel light type (experimental feature)
    0 = Warm White
    1 = Cold White
    SetOption108 0 = Teleinfo telemetry only sent into Energy MQTT JSON (default)
    1 = Each Teleinfo received frame is also sent by MQTT (mainly to be able to display real time data)
    SetOption109 0 = (default)
    1 = force gen1 Alexa mode, for Echo Dot 2nd gen devices only
    SetOption113 works only with rotary dial button
    0 = (default)
    1 = set dimmer low on rotary dial after power off
    SetOption114 Detach switches from relays and send MQTT messages instead
    0 = disable (default)
    1 = enable
    Example result: {"Switch1":{"Action":"ON"}}
    SetOption115 ESP32 MI32 BLE
    0 = disable (default)
    1 = enable
    SetOption116 Auto-query of lights and devices
    1 = disable
    SetOption117 Run fade at fixed duration instead of fixed slew rate
    1 = enable
    SetOption123 Wiegand tag number output in hex format
    1 = enable
    SetOption124 Wiegand key pad stroke format
    0 = one tag (ending char # or ) *(default)
    1 = one key
    SetOption125 ZbBridge only Hide bridge topic from zigbee topic (use with SetOption89)
    1 = enable
    SetOption126 Enable arithmetic mean over teleperiod for JSON temperature for DS18x20 sensors
    1 = enable
    SetOption127 Force Wi-Fi in no-sleep mode even if Sleep 0 is not enabled
    1 = enable
    SetOption128 Web referrer check for HTTP API commands
    0 = disabled
    1 = enabled (default)
    SetOption129 Enable split total energy results #13030
    1 = enable
    SetOption130 Add heap size (and ESP32 fragmentation) to logging timestamp for debugging
    1 = enable
    SetOption131 (Tuya) Allow save dimmer = 0 received by MCU
    1 = enable
    SetOption132 When MQTT TLS is enabled, forces fingerprint validation of server identity instead of checking the identify against a certificate authority (CA)
    1 = Fingerprint, 0 = CA
    SetOption134 PWM force phases to be synced (ESP32 only).
    On ESP32, PWM phases are by default distributed one after the other to minimize effect on power supply. This is also mandatory for H-Bridge devices.
    0 = phases are automatically aligned one after the other, 1 = phases all start at the same time (default behavior for ESP8266).
    SetOption135 Disables Display Splash screen (for all drivers, universal & LVGL)
    1 = Splash screen disabled, 0 = Splash screen displayed
    SetOption136 1 = Disable single sensor reports from Tuya devices while keeping teleperiod reports
    0 = Publish an immediate tele/%topic%/SENSOR TuyaSNS message at each reception of individual value (default)
    SetOption137 1 = following Tuya responses will not be forwarded to MQTT when SetOption66 is enabled
      - heartbeat every 10 seconds, TUYA_CMD_HEARTBEAT
      - the WiFi state during start-up and Wi-Fi events, TUYA_CMD_WIFI_STATE
      - the local time info query of the MCU every minute, TUYA_CMD_SET_TIME
      - the received update package info from MCU during firmware update of Tuya MCU, TUYA_CMD_UPGRADE_PACKAGE
    SetOption138 Align GUI energy multicolumn layout in webUI
    0 = left/center (default)
    1 = right
    SetOption139 When SetOption24 1 switch pressure unit to:
    0 = mmHg (default)
    1 = inHg
    SetOption140 0 = open clean MQTT session (default)
    1 = open persistent MQTT session
    SetOption141 1 = disable display of model name in webUI header
    SetOption142 1 = wait 1 second for WiFi connection solving some FRITZ!Box modem issues
    SetOption143 1 = disables ZigBee auto-probing and configure back attribute reporting
    SetOption144 1 = include a timestamp in ZbReceived messages
    SetOption146 1 = enable display of ESP32 internal temperature
    SetOption147 1 = disable publish SSerialReceived MQTT messages, you must use event trigger rules instead
    SetOption148 1 = enable autorun of ArtNet mode at start. If for any reason listening to UDP port fails, this mode is disabled
    SetOption149 1 = DNS try to resolved the address as IPv6 first and IPv4 if none is found. Default behavior is to look for IPv4 address first. See IPv6
    SetOption150 1 = Force no voltage/frequency common in Energy driver (to be detailed)
    SetOption151 1 = Matter support enabled, 0 = Matter support disabled which frees so memory
    SetOption152 1 = Switch between two (0) or one (1) pin bistable relay control (Power, to be detailed)
    SetOption153 1 = Disable running (Berry) autoexec.be on restart
    SetOption154 1 = Handle Berry led using RMT0 as additional WS2812 scheme
    SetOption155 1 = (ZCDimmer) Enable rare falling Edge dimmer instead of leading edge

    TuyaMCU~

    Command Parameters
    TuyaEnum<x> Send value to an Enum (fnId 61, 62, 63 and 64) where<x> = number of Enum
    <value> = must be from a range set in TuyaEnumList
    TuyaEnumList Declare the range an Enum (fnId 61, 62, 63 and 64) must respect (0 is always the first item in range)
    <enum>,<range> = <enum> is Enum<x> declared using TuyaMCU and <range> can be 0..31
    Without payload returns the configuration of all the Enums
    TuyaMCU Used to map functions in TuyaMCU
    <fnId>,<dpId> = read more...
    <fnId>,0 = remove setting for fnId
    TuyaRGB Set correct format of color reporting by tuyaMCU
    0 - Type 1, 12 characters uppercase. Example: 00DF00DC0244 (default)
    1 - Type 1, 12 characters lowercase. Example: 008003e8037a
    2 - Type 2, 14 characters uppercase. Example: 00FF00FFFF6464
    3 - Type 2, 14 characters lowercase. Example: 00e420ffff6464
    TuyaSend<x> Send data to MCU with TuyaMCU
    x = 0..4,8
    TuyaSend0 = send a query command to the MCU
    TuyaSend1 <dpId>,<boolean> = send boolean (0/1) data type to dpId (1 byte max length)
    TuyaSend2 <dpId>,<int> = send integer data to dpId (4 bytes max length)
    TuyaSend2 <dpId>,<0xAABBCCDD> = send 4 byte data to dpId (4 bytes max length)
    TuyaSend3 <dpId>,<value> = send an ASCII string to dpId (unknown max length)
    TuyaSend4 <dpId>,<enum> = send enumerated (0/1/2/3/4/5) data type to dpId (1 byte max length)
    TuyaSend5 <dpId>,<value> = send an HEX string to dpId - 0x prefix NOT needed - (unknown max length)
    TuyaSend6 <dpId>,<value> = send an HEX raw value to dpId - 0x prefix NOT needed, but will be processed correctly - (unknown max length)
    TuyaSend8 = request dpId states if supported
    TuyaTempSetRes Set resolution only for Tuya Set Temperature sensor (fnId 72).
    0..3 = maximum number of decimals shown
    See also SetOption8 - change temperature display unit
    SetOption66 - publish TuyaReceived output to MQTT
    DimmerRange - to adjust dimmer range
    TempRes - set number of decimals shown for temperature sensors

    Serial Bridge~

    Hardware Serial Bridge uses GPIO1 (Tx) and GPIO3 (Rx) or GPIO13 (Tx) and GPIO15 (Rx) pins of your device. Software Serial Bridge can use any other GPIO to be configured as components Serial Tx and Serial Rx (or SerBr Tx and SerBr Rx). If Tx and Rx components are not assigned in the Template or Module, GPIO1 and GPIO3 will be used. Note that changing serial logging (SerialLog 0) will disable the hardware Serial Bridge.

    Information received by Tasmota over the serial bridge is captured automatically. Before data will be received, a properly formatted SerialSend<x> or SSerialSend<x> command must be executed. This must be done any time the device restarts (e.g., via a System#Boot triggered rule). This command is required in order to set how the expected serial data will be formatted and interpreted (i.e., which option). A {"SSerialReceived":{"Data":"<string>"}} message will be posted. You can use a rule to process the string which will be contained in SSerialReceived#Data.

    Hardware Serial Buffer can be configured by SerialBuffercommand, software serial buffer is fixed to 256 bytes long.

    You could activate SetOption147 to disable publishing SSerialReceived MQTT messages. If disabled, you must use event trigger rules instead (SSerialReceived#Data=<string>) to control what, when and how is being published to your MQTT broker or whatever you want.

    Expect possible communication errors when additional sensors are configured.

    Command Parameters
    Baudrate 1 = set hardware serial bridge to default baud rate of 115200 bps
    <value> = set baud rate. The set rate will be a multiple of 300. The maximum baud rate possible is 19,660,500.
    SBaudrate 1 = set software serial bridge to default baud rate of 9600 bps
    <value> = set baud rate. The set rate will be a multiple of 300. The maximum baud rate possible is 19,660,500.
    SerialBuffer 256..520 = set the serial buffer size. This option will not be persisted, use a rule with a trigger like Power1#Boot when you want this to survive a reboot. Sometimes, serial buffer overruns can be mitigated by setting this to a large value such as 520.
    SSerialBuffer 256..SERIAL_BRIDGE_BUFFER_SIZE = set the serial bridge buffer size. This option will not be persisted, use a rule with a trigger like Power1#Boot when you want this to survive a reboot
    SerialConfig value = set serial protocol using data/parity/stop conventional notation (example: 8N1 or 702)
    0..23 = set serial protocol (3 equals 8N1)
    SerialDelimiter <value> = set serial delimiter to escape character code or ASCII character
    1..127 = set serial delimiter to decimal ASCII
    128 = only allow ASCII characters 32 to 127 in response text
    254 = disable serial delimiter & post HEX string
    129..253 or 255 = disable serial delimiter (default = 255)
    SerialSend<x> <string>
    Disable serial logging and send using hardware serial
    x = 1..5
    1 = send appending \n (newline) ()
    2 = send
    3 = replace escape characters and send
    4 = send as binary. Data in serial response messages is encoded as binary strings
    5 = send as hex. Data in serial response messages is encoded as hex strings
    6 = send as comma-delimited string of decimal numbers
    SSerialConfig value = set serial protocol using data/parity/stop conventional notation (example: 8N1 or 702)
    0..23 = set serial protocol (3 equals 8N1)
    SSerialSend<x> <string>
    Send using software serial protocol
    x = 1..5
    1 = send appending \n (newline) ()
    2 = send
    3 = replace escape characters and send
    4 = send as binary data. Data in serial response messages is encoded as binary strings
    5 = send as hex. Data in serial response messages is encoded as hex strings
    6 = send as comma-delimited string of decimal numbers
    9 = enable Serial Bridge console Tee for debugging purposes (payload 1 to enable)

    RF Bridge~

    Command Parameters
    RfCode Show last sent 24-bit user code
    1..8388607 = send 24-bit user code
    #1..#7FFFFF = send 24-bit hexadecimal user code using RfSync, RfLow and RfHigh timing
    RfHigh 1 = reset high pulse time to 840 microseconds
    2..32767 = set high pulse time in microseconds
    #2..#7FFF = set high pulse time in hexadecimal microseconds
    RfHost Show 16-bit host part of user code
    1 = reset 16-bit host part of user code to 11802 (#2E1A)
    2..32767 = set 16-bit host part of user code
    #2..7FFF = set 16-bit host part of user code in hexadecimal
    RfKey<x> Send learned or default RF data for RfKey<x> (x = 1 – 16)
    1 = send default RF data for RfKey<x> using RfSync, RfLow, RfHigh and RfHost parameters
    2 = learn RF data for RfKey<x>
    3 = unlearn RF data for RfKey<x>
    4 = save RF data using RfSync, RfLow, RfHigh and last RfCode parameters
    5 = show default or learned RF data
    6 = send learned RF data
    RfLow 1 = reset low pulse time to 270 microseconds
    2..32767 = set low pulse time in microseconds
    #2..#7FFF = set low pulse time in hexadecimal microseconds
    RfRaw This command only works when the firmware has been updated with Portisch firmware. Refer to the Portisch wiki for details.
    Learning and Decoding RF Codes with Portisch Firmware
    0 = Set iTead default firmware support and messages (default on restart)
    1 = set Portisch firmware support and messages
    166 or AAA655 = start sniffing/reading RF signals disabling iTead default RF handling
    167 or AAA755 = stop sniffing/reading RF signals enabling iTead default RF handling
    168 or AAA855 = transmitting iTead default RF protocols
    169 or AAA955 = start sniffing and learning predefined protocols
    176 or AAB055 = bucket Transmitting using command 0xB0
    177 or AAB155 = start Bucket sniffing using command 0xB1
    192 or AAC000C055 = beep (00C0 is the length of the sound)
    255 or AAFF55 = show Rf firmware version (result AA02FF means Version 02)
    <value> = hexadecimal data to be sent to RF chip. This must be immediately followed by the RfRaw 0 command (e.g., Backlog RfRaw <value>; RfRaw 0
    RfSync 1 = reset start sync pulse time to 8470 microseconds
    2..32767 = set start sync pulse time in microseconds
    #2..#7FFF = set start sync pulse time in hexadecimal microseconds
    RfTimeOut change timeout in RfReceive
    100..60000 = disable duplicate RfReceive (default = 1000)
    See also SetOption28 - Set RF received data format

    RF Transceiver~

    Command Parameters
    RfSend <value> = code decimal or JSON. Data value is required and can be decimal or hexadecimal (using the 0x prefix), other values are optional.

    JSON
    {"Data":"<value>","Bits":<value>,"Protocol":<value>,"Pulse":<value>}
    "Data":"<value>" = hexadecimal code
    "Bits":<value> = required number of data bits (default = 24)
    "Protocol":<value> = protocol number (default = 1)
    "Repeat":<value> = repeat value (default = 10)
    "Pulse":<value> = pulse value (350 = default for protocol 1)
     e.g., RfSend {"Data":"0x7028DC","Bits":24,"Protocol":1,"Pulse":238}

    Decimal
    data, bits, protocol, repeat, pulse
     e.g., RfSend 7350492, 24, 1, 10, 238 or RfSend 0x7028DC, 24, 1, 10, 238

    IR Remote~

    The standard Tasmota builds have reduced support for IR protocols: RC5, RC6 and NEC. Use Tasmota-IR to have access to full protocols.

    See Codes for IR Remotes.

    Command Parameters
    IRSend<x> Send an IR remote control code as a decimal or hexadecimal string in a JSON payload. In order to send IR data, you must configure at least one of the free device GPIOs as IRSend (8). GPIO01 nor GPIO03 can be used.
    <x> [optional] = number of times the IR message is sent. If not specified or 0..1, the message is sent only once (i.e., not repeated) (default)
    >1 = emulate a long-press on the remote control, sending the message <x> times, or sending a repeat message for specific protocols (like NEC)

    {"Protocol":"<value>","Bits":<value>,"Data":<value>, "Channel":<value>}

    "Protocol" (select one of the following):
    • "NEC"
    • "RC5"
    • "RC6"
    "Bits":1..32 = required number of data bits
        for PANASONIC protocol this parameter is the the address, not the number of bits

    "Data":1..(2^32)-1 = data frame as 32 bit decimal.
        e.g., IRSend {"Protocol":"NEC","Bits":32,"Data":2170978686}
    or
    "Data":0x1..0xFFFFFFFF = data frame as 32 bit hexadecimal.
        e.g., IRSend {"Protocol":"NEC","Bits":32,"Data":0x8166817E}
    "Channel":1..16 = IRSend GPIO to be used to send the message.

    Alternatively, you can send IR remote control codes using RAW command encoding.

    Read more...

    Tasmota-IR enabled with all protocols

    Command Parameters
    IRSend<x> <x> [optional] = number of times the IR message is sent. If not specified or 0..1, the message is sent only once (i.e., not repeated) (default)
    >1 = emulate a long-press on the remote control, sending the message <x> times, or sending a repeat message for specific protocols (like NEC)

    {"Protocol":"<value>","Bits":<value>,"Data":<value>,"DataLSB":<value>,"Repeat":<value>}

    "Protocol" or "Vendor" (select one of the following):
    RC5, RC6, NEC, SONY, PANASONIC, JVC, SAMSUNG, WHYNTER, AIWA_RC_T501, LG, MITSUBISHI, DISH, SHARP, DENON, SHERWOOD, RCMM, SANYO_LC7461, RC5X, NEC (non-strict), NIKAI, MAGIQUEST, LASERTAG, CARRIER_AC, MITSUBISHI2, HITACHI_AC1, HITACHI_AC2, GICABLE, LUTRON, PIONEER, LG2, SAMSUNG36, LEGOPF, INAX, DAIKIN152

    "Bits":1..64 = required number of data bits
        for PANASONIC protocol this parameter is the the address, not the number of bits

    "Data":0x1..0xFFFFFFFFFFFFFFFF = data frame as 64 bit hexadecimal.
        e.g., IRSend {"Protocol":"NEC","Bits":32,"Data":0x8166817E}
    Or
    "DataLSB":0x1..0xFFFFFFFFFFFFFFFF = data frame as 64 bit hexadecimal with LSB (each byte with bits reversed).
        e.g., IRSend {"Protocol":"NEC","Bits":32,"Data":0x8166817E}
    DataLSB comes handy with LSB-first (Least Significant Bit First) protocols like NEC, and makes decoding/encoding easier.

    "Repeat":0..<x> if 0 send the frame once, if >0 simulates a long press; Note: "Repeat":1 sends the message twice.

    Alternatively, you can send IR remote control codes using RAW command encoding.
    See also
    SetOption29 - Set IR received data format
    SetOption38 - Set IR received protocol sensitivity
    SetOption58 - IR Raw data in JSON payload
    IRHVAC Send HVAC IR remote control code as JSON payload

    IRHVAC {"Vendor":"Mitsubishi_Heavy_152", "Power":"On","Mode":"Hot","FanSpeed":3,"Temp":22.5}

    "Protocol" or "Vendor" (select one of the following):
    COOLIX, DAIKIN, KELVINATOR, MITSUBISHI_AC, GREE, ARGO, TROTEC, TOSHIBA_AC, FUJITSU_AC, MIDEA, HAIER_AC, HITACHI_AC, HAIER_AC_YRW02, WHIRLPOOL_AC, SAMSUNG_AC, ELECTRA_AC, PANASONIC_AC, DAIKIN2, VESTEL_AC, TECO, TCL112AC, MITSUBISHI_HEAVY_88, MITSUBISHI_HEAVY_152, DAIKIN216, SHARP_AC, GOODWEATHER, DAIKIN160, NEOCLIMA, DAIKIN176, DAIKIN128

    "Model": Some HVAC have variants in protocols, this field allows to specify the variant, see detailed list.
    • Fujitsu_AC: ARRAH2E|ARDB1
    • Panasonic_AC: LKE|NKE|DKE|JKE|CKP|RKR
    • Whirlpool_AC: DG11J13A|DG11J104|DG11J1-04|DG11J191
    "Power":
    • On, Yes, True, 1
    • Off, No, False, 0
    "Mode":
    • Off, Stop
    • Auto, Automatic
    • Cool, Cooling
    • Heat, Heating
    • Dry, Drying, Dehumidify
    • Fan, Fanonly, Fan_Only
    "FanSpeed":
    • Auto, Automatic
    • Min, Minimum, Lowest, 1
    • Low, 2
    • Med, Medium, Mid, 3
    • High, Hi, 4
    • Max, Maximum, Highest, 5
    "SwingV": vertical swing of Fan
    • Auto, Automatic, On, Swing
    • Off, Stop
    • Min, Minimum, Lowest, Bottom, Down
    • Low
    • Mid, Middle, Med, Medium, Centre, Center
    • High, Hi
    • Highest, Max, Maximum, Top, Up
    "SwingH": horizontal swing of Fan
    • Auto, Automatic, On, Swing
    • Off, Stop
    • LeftMax, Left Max, MaxLeft, Max Left, FarLeft, Far Left
    • Left
    • Mid, Middle, Med, Medium, Centre, Center
    • Right
    • RightMax, Right Max, MaxRight, Max Right, FarRight, Far Right
    • Wide
    "Celsius": temperature is in Celsius ("On") or Fahrenheit ("Off")
    "Temp": Temperature, can be float if supported by protocol
    "Quiet": Quiet mode ("On" / "Off")
    "Turbo": Turbo mode ("On" / "Off")
    "Econo": Econo mode ("On" / "Off")
    "Light": Light ("On" / "Off")
    "Filter": Filter active ("On" / "Off")
    "Clean": Clean mode ("On" / "Off")
    "Beep": Beep active ("On" / "Off")
    "Sleep": Timer in seconds
    "StateMode":
    • SendOnly (default)
    • StoreOnly
    • SendStore
    See also
    SetOption29 - Set IR received data format
    SetOption38 - Set IR received protocol sensitivity
    SetOption58 - IR Raw data in JSON payload

    Displays~

    Command Parameters
    Display Show current display setting as a JSON payload
    DisplayAddress 0..255 Set display module address
    DisplayDimmer 0 = Turn the display off
    1..100 = Set display luminosity (only on 8x8 Dot-Matrix displays)
    13..100 maps to 1..7 levels of brightness for TM1637, TM1638 and MAX7219 or TM1650 seven-segment display modules
    DisplayInvert 1 - Invert display where implemented. More info...
    DisplayMode 0..5 Set to display predefined content according to display type
    0..3 for TM1637, TM1638 and MAX7219 or TM1650 seven-segment display modules
    DisplayModel Set display model:
    1 = I2C LCD Display (default addresses 0x27, 0x3F)
    2 = SSD1306 OLED 128x32/128x64/68x48 (default I2C addresses 0x3C, 0x3D)
    3 = HT16K33 8x8 Dot-Matrix
    4 = ILI9341 TFT LCD
    5 = 2.9 inch E-Paper Display 296x128 (software 3-wire SPI)
    6 = 4.2 inch E-Paper Display 400x300 (software 3-wire SPI)
    7 = SH1106 OLED 128x64 (default I2C address 0x3c)
    8 = ILI9488 TFT 480x320 (capacitive touch, hardware 3-wire SPI)
    9 = SSD1351 color OLED 128x128 (hardware 3-wire SPI)
    10 = RA8867 TFT LCD 1024x600 (capacitive touch, hardware 4-wire SPI)
    15 = TM1637 7-segment, 4-,6- and 8-digit displays (TM1637, TM1638 and MAX7219 or TM1650), hardware 2- and 3-wire I2C-like interface
    16 = LilyGO T5-4.7 E-Paper display board
    17 = Universal Display Driver powered displays
    18 = Interface to virtual display driver with Berry
    19 = MAX7219 Dot Matrix
    20 = TM1650 7-segment 4-digit displays, I2C
    DisplayRefresh 1..7 Set time in seconds to update predefined content when using DisplayMode0
    DisplaySize 1..4 Set display scale-up size (SSD1306 and ILI9341 only)
    DisplayRotate Set rotation angle
    0 = 0°
    1 = 90°
    2 = 180°
    3 = 270°
    DisplayText <value> = See DisplayText use
    For TM1637, TM1638 and MAX7219 or TM1650, see below
    DisplayText
    (TM1637, TM1638 and MAX7219 or TM1650)
    text[, position[, length]]
    Clears and then displays basic text on the 7-segment display.

    length can be 1 to NUM_DIGITS ,
    position can be 0 (left-most) to NUM_DIGITS-1 (right-most)

    A caret(^) symbol in the text input is displayed as the degrees(°) symbol. This is useful for displaying Temperature (or angle)!
    See TM163x or TM1650 for details.
    DisplayTextNC
    (TM1637, TM1638 and MAX7219 or TM1650)
    text[, position[, length]]
    Clears first, then displays text. Usage is same as above.
    See TM163x or TM1650 for details.
    DisplayType Select display sub-modules. More info...
    For usage of this command with TM163x or TM1650, see TM163x or TM1650 for details.
    DisplayCols 1..44 Set number of display columns (for display modes>0)
    DisplayRows 1..32 Set number of display rows (for display modes>0)
    DisplayFont Specify the current font
    0 use classic GFX font
    1 = 12
    2 = 24
    3 = 8 (opt)
    7 use RA8876 internal font
    DisplayWidth Specify the display width in pixels (SSD1306 only)
    -or-
    Specify number of digits in TM163x seven-segment display module
    DisplayHeight Specify the display height in pixels (SSD1306 only)
    DisplayClear
    (TM1637, TM1638 and MAX7219 or TM1650)
    Clears the display.
    See TM163x or TM1650 for details.
    DisplayNumber
    (TM1637, TM1638 and MAX7219 or TM1650)
    num [, position[, leading_zeros[, length]]]
    Clears and then displays number num without decimal.

    leading zeros can be 1 or 0 (default),
    length can be 1 to NUM_DIGITS (4 or 6),
    position can be 0 (left-most) to NUM_DIGITS (right-most).
    See TM163x or TM1650 for details.
    DisplayNumberNC
    (TM1637, TM1638 and MAX7219 or TM1650)
    num [, position[, leading_zeros[, length]]]
    Display integer number as above, but without clearing first. Usage is same as above.
    See TM163x or TM1650 for details.
    DisplayFloat
    (TM1637, TM1638 and MAX7219 or TM1650)
    num[, position[, precision[, length]]]
    Clears and then displays float (with decimal point).

    precision can be 0 to NUM_DIGITS (default),
    length can be 1 to NUM_DIGITS (4 or 6),
    position can be 0 (left-most) to NUM_DIGITS (right-most).
    See TM163x or TM1650 for details.
    DisplayFloatNC
    (TM1637, TM1638 and MAX7219 or TM1650)
    num[, position[, precision[, length]]]
    Displays float (with decimal point) as above, but without clearing first. Usage same as above.
    See TM163x or TM1650 for details.
    DisplayRaw
    (TM1637, TM1638 and MAX7219 or TM1650)
    position, length, num1 [, num2[, num3[, num4[, ...upto NUM_DIGITS numbers]]...]
    Takes upto NUM_DIGITS comma-separated integers (0-255) and displays raw segments.

    length can be 1 to NUM_DIGITS (4 or 6),
    position can be 0 (left-most) to NUM_DIGITS (right-most).
    num1, num2, ... are numbers representing a 7-segment digit. Each number represents all segments of one digit.
    Segment a=1, b=2, c=4, d=8, e=16, f=32, g=64 and h (decimal point)=128.
    To turn on all segments, the number would be 1+2+4+8+16+32+64+128 = 255.
    See TM163x or TM1650 for details.
    DisplayScrollText
    (TM1637, TM1638 and MAX7219 or TM1650)
    text [, num_iterations]
    Displays scrolling text, upto 50 characters.
    If num_iterations is not specified, it scrolls indefinitely, until another Display- command is issued. Optionally, specifying num_iterations causes the scrolling to stop after the specified number of iterations.
    See TM163x or TM1650 for details.
    DisplayScrollDelay
    (TM1637, TM1638 and MAX7219 or TM1650)
    0..15 Sets the speed of text scroll. Smaller delay implies faster scrolling.

    See TM163x or TM1650 for details.
    DisplayLevel
    (TM1637, TM1638 and MAX7219 or TM1650)
    0..100 Display a horizontal bar graph.

    See TM163x or TM1650 for details.
    DisplayClock
    (TM1637, TM1638 and MAX7219 or TM1650)
    Displays a clock.
    1 = displays a clock in 12-hour format.
    2 = displays a clock in 24-hour format.
    0 = turns off the clock and clears the display
    See TM163x or TM1650 for details.

    Shutters~

    Command
    ESP8266:(x = 0..4), ESP32(x = 0..16) (0=set value to all defined shutters)
    Parameters
    ShutterMode 1..7 (default = 1)
    Defines the mode the shutter will operates the relays, steppers and/or servos. 6=autodetect based on INTERLOCK and GPIO defined. STATUS 13 show the mode.
    1 = normal two relay up/off down/off
    2 = two relay on/off up/down. Must be set manually
    3 = one relay garage mode
    4 = one relay plus stepper motor
    5 = one relay and position servo
    6 = one relay and PWM servo speed controlled by PWM. Position time based
    7 = autodetect (once) based on existance of GPIO definition.
    ShutterButton<x> <button> <func> <mqtt>

    Assign a button to control the shutter. For more details please refer to Blinds and Shutters support. Here you can define exact positions (and tilt positions ;ESP32 only) on the events single/double/tripple press

    <button>
    0: disable buttons for this shutter
     ESP8266:1..4, ESP32:1..32: Button number
    <func> up/down/updown/toggle: function to assign to the button
    <mqtt> 1/0: enable/disable MQTT publish for button hold action

    For example:
  • To control shutter #1 by two buttons: Backlog ShutterButton1 1 up 1; ShutterButton1 2 down 1 assigns button #1 to act as an "up" button (1x press open, 2x press 50% position, 3x press 74% position) and button #2 to act as a "down" button (1x press close, 2x press 50% position, 3x press 24% position) for shutter #1 including MQTT publish.
  • To control shutter #1 by a single button: ShutterButton1 1 updown 0 assigns button #1 to act as an "up and down" button (1x press up, 2x press down).
  • To control shutter #1 by a single button: ShutterButton1 1 toggle 0 assigns button #1 to act as a "toggle" button (1x press toggle, 2x press 50% position).
  • ShutterCalibration<x> Granular shutter position calibration. The measured opening position of the shutter at the 30, 50, 70, 90, and 100 percent opened locations. For example: ShutterCalibration<x> 23 38 56 74 82
    ShutterChange<x> -100..100 Moves the shutter from the current position relatively in %. If the resulting position is below 0 or above 100 it will be capped. Command can also be executed during movement and will change the target position.
    ShutterCloseDuration<x> 1.0 ..240.0 (default = 10.0)
    time, in seconds, it takes to fully close the shutter. A fraction of a second can be specified (e.g. 45.7).
    ShutterClose<x> Engage the relay to close the shutter. This action can be requested at any time. Number of shutter can be the index or the argument
    ShutterFrequency<x> 0..10,000Hz (default = 1000)
    the maximum frequency at which the stepper motor can operate reliably. Typically this is up to 2,000Hz with a 12V power supply and up to 5,000Hz with a 24V power supply.
    ShutterEnableEndStopTime<x> 0 = no additional shutter end stop time (default)
    1 = 1 s additional shutter end stop time
    ShutterInvert<x> 0 = use default shutter positioning (0 = Closed, 100 = Open)
    1 = invert shutter positioning (100 = Closed, 0 = Open) (e.g., if used with KNX)
    ShutterInvertWebButtons<x> 0 = use default button icons (▲ for open, ▼ for close)
    1 = invert button icons (▼ for open, ▲ for close) (e.g., if used with horizontal awning: where open means rolling-down fabric material and close rolling-up in a protect position)
    ShutterLock<x> 0 = unlock shutter positioning (default)
    1 = lock shutter positioning
    ShutterMotorDelay<x> -12.75 .. 12.75 (default = 0)
    time, in seconds, it takes the motor to start moving once power is turned on; i.e., motor lag time. You can use negative numbers if your motor stops to late after power OFF

    When used with stepper motors, this setting defines the ramp up/down speed (i.e., acceleration/deceleration) before the motor reaches its target speed for gradual starting and stopping. In this case only positive numbers are allowed.
    ShutterMotorStop 0 .. 64000 (default = 500)
    time in milliseconds, one relay must be off before the other one can start. This control the OFF time on TOGGLE and POSITION change commands that require a TOGGLE. Also in GarageMode this sets the wait time between two commands. Equal for all shutters.
    ShutterOpenDuration<x> 1.0 ..240.0 (default = 10.0)
    time, in seconds, it takes to fully open the shutter. A fraction of a second can be specified (e.g. 45.7).
    ShutterOpen<x> Engage the relay to open the shutter. This action can be requested at any time. Number of shutter can be index or the argument
    ShutterPosition<x> 0..100 ,-90..90, UP, OPEN, DOWN, CLOSE, STOP, TOGGLE, TOGGLEDIR, STOPOPEN, STOPCLOSE,
    A shutter position change can be requested at any time. The shutter will stop and revert or update to the requested position. The shutter's actual position will be saved after the movement is completed. In this case, the position will be restored during reboot. An interruption during shutter movement (e.g., a device restart) will lose the current position. Optional the position can be enhances with the position of the tilt. This only works with numerical positions. The seperator is a comma ,.
    ShutterPWMRange<x> 0..1023,0..1023
    For servo motors the min and max position is defined by the length of the duty cycle signal. Because every servo is different the min and max PWM value must be set for each servo type. The value is also dependant on the PWMfrequency. Servos normally use 50..200 as PWMfrequency.
    ShutterRelay<x> <value>
    0 = disable this and all higher numbered shutters
    Relay<value> component used to open the shutter. This relay's mate, the next higher numbered relay, closes the shutter. Depending on the shutter mode, the relays may need to be interlocked using the Interlock command.
    The ShutterRelay command must be executed first before any other shutter commands for Shutter<x> can be executed.
    ShutterSetClose<x> shutter closed position. ShutterPosition will be reset to fully closed value (e.g., 0 when ShutterInvert = 0, 100 otherwise). This does not work with Servos. min and max of servos are always defined through ShutterPWMRange.
    ShutterSetOpen<x> shutter opened position. ShutterPosition will be reset to fully opened value (e.g., 100 when ShutterInvert = 0, 0 otherwise). This does not work with Servos. min and max of servos are always defined through ShutterPWMRange.
    ShutterSetHalfway<x> 0..100 (default = 50)
    Define shutter half open position (in percent)
    ShutterStop<x> Disengage the relays to stop the shutter. Number of shutter can be the index or the argument
    ShutterStopClose<x> Stop the shutter when currently moving, close it otherwise
    ShutterStopOpen<x> Stop the shutter when currently moving, open it otherwise
    ShutterStopPosition<x> Stop the shutter when currently moving, set it to position 0..100, UP, DOWN, STOP, TOGGLE otherwise
    ShutterStopToggle<x> Stop the shutter when currently moving, do ShutterToggle otherwise
    ShutterStopToggleDir<x> Stop the shutter when currently moving, do ShutterToggleDir otherwise
    ShutterToggle<x> Toggle the shutter - close the shutter when its position is >50, open it otherwise
    ShutterToggleDir<x> Toggle the shutter - close the shutter when it previously moved to open, open it otherwise
    ShutterTiltConfig<x> <min> <max> <Tiltduration> <openposition> <closeposition> (default = 0 0 0 0 0)
    Configure the tilt for venetian blinds. Min/man values must be in the range of -90° to 90°. Open and Close position must be part of the defined range between min and max. Tiltduration defines the time the shutter needs to change the tilt from min to max value. This time has to been multiplied by 20. E.g. 1.2sec = 1.2 x 20 = 24. Example defines tilt on shutter 2: shuttertiltconfig2 -90 90 24 0 90
    ShutterTilt<x> Set the tilt position <value> (between min and max), OPEN, CLOSE. Definition please see shuttertiltconfig
    ShutterTiltChange<x> -100..100 Moves the shuttertilt from the current position relatively in %. If the resulting tilt is below min or above max it will be capped. Command can also be executed during movement and will change the tilt at target position.
    See also SetOption80 - Enable shutter support

    Zigbee~

    See Zigbee article for more information

    Command Parameters
    <device> As <device> in following commands you can use interchangeably:
    <shortaddr> = short address of the Zigbee device on the network, example: 0x1234
    <longaddr> = permanent IEEE address of the Zigbee device (64 bits), example: 0x00158D00041160C5
    <index> = number of the device in the internal list (starts at 1), ideal for enumerating devices, example: 3 for third device in the list
    <name> = friendly name. Only when previously set with ZbName
    ZbBind Binds one Zigbee device to another device or to a group. This allows one device to directly send commands (f.e. a remote to a bulb) without any action on the coordinator.
    Command structure: {"Device":"<device>", "Endpoint":<endpoint>, "Cluster":<cluster>, "ToDevice":"<to_device>", "ToEndpoint":<to_endpoint>, "ToGroup":<to_group> }
    <device> = device sending messages (mandatory)
    <endpoint> = source endpoint (mandatory)
    <cluster> = source cluster id (mandatory)
    <to_device> = target device (optional)
    <to_endpoint> = target endpoint
     (optional if it can be inferred from ZbStatus3)
    <to_group> = target group id (optional)
    📓 You must specify either "ToDevice" or "ToGroup" but not both
    📓 Zigbee2Tasmota must know the IEEE address of target device, see ZbStatus2 to verify and ZbProbe to have Zigbee2Tasmota query the address
    (EZSP ZBBridge only) If you bind devices to groups you should also use ZbListen to that group, otherwise MQTT messages will not be published
    ZbBindState Asks the device for its internal binding states
    <device> the device to query
    <n> the start index for the request, 1 is the default. This is used to scan through all bindings.
    ZbCIE Configure on the ZigBee device the CIE address using the IEEE address of the ZigBee Bridge. The ZigBee Bridge will act as the CIE device for the ZigBee Network. See IAS Cluster in the ZigBee specification for more information.
    Usage: ZbCIE <device>,<endpoint>
    <device> is the ZigBee device to configure
    <endpoint> is the endpoint in the ZigBee device where the IAS Cluster is.
    Example: ZbCIE 1,44
    ZbEnroll Enroll the the ZigBee device to the ZigBee Bridge for reporting security sensors data, panels, etc. The ZigBee Bridge will act as the CIE device for the ZigBee Network.
    Usage: ZbEnroll <device>,<endpoint>
    <device> is the ZigBee device to configure
    <endpoint> is the endpoint in the ZigBee device where the IAS Cluster is.
    Example: ZbEnroll 1,44
    ZbConfig display the current Zigbee configuration
    Example or result: {"ZbConfig":{"Channel":11,"PanID":"0x1A63","ExtPanID":"0xCCCCCCCCCCCCCCCC","KeyL":"0x0F0D0B0907050301","KeyH":"0x0D0C0A0806040200"}}
    ZbConfig <json> change the configuration and restart if any change was applied. Warning: change in configuration causes a reset of the CC2530/ZBBridge and requires devices to be re-paired.
    "Channel":<channel>: Zigbee radio channel (11-26)
    "PanID":<panid>: identifier of the Zigbee Network
    "ExtPanID":<extpanid>: unique identifier of the Zigbee Network (ExtPanID features are not supported in Z2T but this parameter needs to be set)
    "KeyL":<key_l>,"KeyH":<key_h>: 128 bits encryption key, split into 2 64 bits values (Low and High)
    "TXRadio":<txradio>: radio power in dBm (1-20) only for ZBBridge
    All parameters are optional and only the ones specified are changed. The command always displays the complete configuration after the change
    Example of command: ZbConfig {"Channel":22,"PanID":"0x1A69","ExtPanID":"0xDDCCCCCCCCCCCCCC","KeyL":"0xFF0D0B0907050301","KeyH":"0xED0C0A0806040200"}
    The following command creates a highly secure Network key based on a hardware random generator:
    ZbConfig {"KeyL":"","KeyH":""}
    ZbData feature in development
    ZbDeviceTopic
    SetOption89
    Configure MQTT topic for Zigbee devices (also see SensorRetain)
    0 = single tele/%topic%/SENSOR topic (default)
    1 = unique device topic based on Zigbee device ShortAddr
    Example: tele/Zigbee/5ADF/SENSOR = {"ZbReceived":{"0x5ADF":{"Dimmer":254,"Endpoint":1,"LinkQuality":70}}}
    ZbEndpointSuffix
    SetOption101
    Add Zigbee source endpoint as suffix to attributes
    0 = disable (default)
    1 = enable
    e.g. Power3 instead of Power if sent from endpoint 3.
    ZbEndpointTopic
    SetOption120
    Add the Zigbee endpoint as suffix in topic when using SetOption89 1
    0 = disable (default)
    1 = enable
    ZbForget Used for devices that are unused but still visible in ZbStatus
    <device> = Remove a device from the Tasmota flash memory. It does not un-pair the device nor deleting the device information in the CC2530/ZBBridge.
    ZbLight Sets or reads the light type to be emulated in Zigbee Hue Emulation with Alexa.
    <device>,<light_type> sets the light type using an integer 0..5 corresponding to the number of channels (from one channel (on/off) to 5 channel (RGBCCT) lights)
    <device>,-1 removes the device from Philips Hue emulation
    <device> displays the current status of the Light (Zigbee2Tasmota tracks all changes to the light)
    ZbListen (EZSP ZBBridge only)Listens to a multicast group address. By default EZSP will not report group messages unless you subscribe to the group.
    <x>: slot in the array of group addresses, 1..15
    <group>: group address to listen to, 0..0xFFFF
    At start-up, Z2T automatically listens to group 0 in slot 0.
    CC2530 does not need this command and always report all group messages.
    ZbLeave <device> = request a device to leave the network.
    If the device is offline or sleeping, this will have no effect. It is not 100% guaranteed that the device will never connect again
    ZbLoad <file>.zb = load Zigbee definition file
    ZbLoadDump <file>.zb = dump Zigbee definition file contents to console
    ZbInfo <device> = display all information known about a device, equivalent to ZbStatus3 with a simpler JSON output
    ZbMap Asks the device for its view of the Zigbee topology
    <device> the device to query
    <n> the start index for the request, 1 is the default. This is used to scan through all values since devices usually return only 3 values per request.
    ZbName Sets or reads the Zigbee device friendly name (up to 32 characters).
    <device>,<name> sets the new friendly name
    <device>, (empty name) clears the friendly name
    <device> displays the current friendly name
    Also see SetOption83 1 to enable friendly names as JSON keys instead of ShortAddr.
    ZbNameKey
    SetOption83
    Uses Zigbee device friendly name instead of 16 bits short addresses as JSON key when reporting values and commands
    0 = JSON key as short address
    1 = JSON key as friendly name
    See ZbName <device>,<name>
    ZbNameTopic
    SetOption112
    0 = (default)
    1 = use friendly name in Zigbee topic (use with ZbDeviceTopic)
    ZbNoAutoBind
    SetOption110
    0 = (default)
    1 = disable Zigbee auto-binding and auto-attribute reporting when pairing a new device. Use only if you want to manually configure devices
    ZbNoPrefix
    SetOption100
    remove Zigbee ZbReceived value from {"ZbReceived":{xxx:yyy}} JSON message
    0 = disable (default)
    1 = enable
    ZbOccupancy Configure the time-out after "Occupancy":1 to send a synthetic "Occupancy":0 for Zigbee motion sensors
    <device>,<x> - set occupancy timeout for <device>
    Possible values for <x>
    0: no time-out, the device actually generates "Occupancy":0
    n: the number of seconds. The possible values are 15, 30, 45, 60, 75, 90, 105, 120. If the number is different, it is rounded up
    -1: apply the default of 90 seconds
    ZbOmitDevice
    SetOption119
    Remove device addr from JSON payload
    0 = disable (default)
    1 = enable
    ZbPermitJoin Sets pairing mode for new device discovery
    0 = disable pairing mode
    1 = enable pairing mode for 60 seconds
    99 = enable pairing until device reboots (CC2530 only)
    🚨 Leaving Zigbee network open to join will allow any Zigbee device to connect and retrieve your network encryption key. This can lead to a compromise of your Zigbee network.
    ZbPing <device> = test availability of Zigbee device. If the device is connected and not sleeping, you should receive a ZbPing message within the next second.
    Example: ZbPing 0x5ADF responds with:
    {"ZbPing":{"Device":"0x5ADF","IEEEAddr":"0x90FD9FFFFE03B051"}}
    ZbReceivedTopic
    SetOption118
    Move ZbReceived from JSON message into the subtopic replacing "SENSOR" default
    0 = disable (default)
    1 = enable
    ZbSend Command structure: {"Device":"<shortaddr>", "Endpoint":"<endpoint>", "Manuf":<manuf>, "Send":{"<sendcmd>":<sendparam>}}
    <shortaddr> = short address of the Zigbee device on the network
    <endpoint> = target endpoint on the device (understanding endpoints)
    <manuf> = (optional) forces a specific ManufacturerId in the ZCL frame (required by some Xiaomi devices)
    "<sendcmd>":<sendparam> = command and parameters to send (Zigbee Device Commands)
    📓 _Use ZbZNPSend to send a raw form low-level message on CC253x gateways _
    Example: ZbSend { "Device":"0x1234", "Endpoint":"0x03", "Send":{"Power":"on"} }
    ZbScan Do an energy scan on each radio channel
    ZbStatus<x> Display Zigbee devices seen on the network since boot
    <device> (optional)
    = all devices
    This command provides three levels of increasing detail according to <x>
    ZbStatus1 Display Short Address, and Friendly Name
    ZbStatus2 Also include Manufacturer ID and Model ID
    ZbStatus3 Also include a list of endpoints and the clusterIds supported by each endpoint
    Example: ZbStatus3 1 requests all details for device number 1
    📓 Requested information may exceed maximum result size allowed by Tasmota. In this case, the output will be truncated. To get all of the desired information, request results for a specific device individually.
    ZbUnbind Unbinds one Zigbee device from another or from a group.
    {"Device":"<device>", "Endpoint":<endpoint>, "Cluster":<cluster>, "ToDevice":"<to_device>", "ToEndpoint":<to_endpoint>, "ToGroup":<to_group> }
    <device> = the device sending the messages (mandatory)
    <endpoint> = the source endpoint (mandatory)
    <cluster> = the source cluster id (mandatory)
    <to_device> = the target device (optional)
    <to_endpoint> = the target endpoint (optional if it can be inferred from ZbStatus3)
    <to_group> = the target group id (optional)
    📓 You must specify either "ToDevice" or "ToGroup" but not both
    📓 Zigbee2Tasmota must know the IEEE address of the target device, use ZbStatus2 to verify and ZbProbe to query the address.
    ZbUnload <file>.zb = unload Zigbee definition file
    See also SetOption83, SetOption89, SetOption100, SetOption101

    Zigbee Debug Functions~

    ⚠ ⚠ ⚠ Do not use unless you know exactly what you are doing. ⚠ ⚠ ⚠

    Command Parameters
    ZbModelId Manually force the ModelId field of a Zigbee device. This is used to simulate devices not physically present on the network, for debugging only.
    <device>,<modelid> = set new ModelId
    <device>, = (empty ModelId) clear ModelId
    <device> = display current ModelId (also displayed in ZbStatus2)
    ZbProbe <device>= probe a Zigbee device to get additional information including its IEEEaddress, vendor and model names, endpoints, and supported clusters per endpoint.
    Device probe is performed automatically when a new Zigbee device connects.
    Battery powered Zigbee devices can not be probed in general because they are usually in sleep mode.
    ZbRead Removed in favor of ZbSend with "Read" attribute.
    ZbReset 1 = perform a factory reset and reconfiguration of the CC2530 chip.
    ⚠ You will need to re-pair all Zigbee devices
    ZbRestore Restores a device configuration previously dumped with ZbStatus2. This command does not pair a device, but lets you get back device configuration like ModelId or IEEEAddress.
    <json> = json contains the fields dumped with ZbStatus2. <json> can contain multiple devices (if they fit).
    ZbSave Forces saving the Zigbee device information to Flash. Auto-saving happens 10 seconds after a new Device parameter was changed, this command is normally not useful
    ZbZNPSend (CC2530 only) Send a raw ZCL message to a Zigbee device. This is a low-level command, and requires to manually build the ZCL parameters. Most common usage will be provided as high-level functions.
    ZbZNPReceive (CC2530 only) Simulates a received message
    <hex> = hex string of the simulated message, same format as ZbZNPReceived debug logs
    ZbEZSPSend (EZSP only) Send a raw EZSP message. This is a low-level command, and requires to manually build the ZCL parameters. Most common usage will be provided as high-level functions.
    <x>: 1=high-level EZSP command, 2=low-level EZSP frame, 3=low-level EZSP/ASH frame
    <hex> = hex string of the message
    ZbEZSPReceive (EZSP only) Simulates a received message
    <x>: 1=high-level EZSP command, 2=low-level EZSP frame, 3=low-level EZSP/ASH frame
    <hex> = hex string of the simulated message, same format as ZbZNPReceived debug logs

    Bluetooth~

    Command Parameters
    HM10Scan Start a new device discovery scan
    HM10Period Show interval in seconds between sensor read cycles. Set to TelePeriod value at boot.
    HM10Baud Show ESP8266 serial interface baudrate (Not HM-10 baudrate)
    <value> = set baudrate
    HM10AT <command> = send AT commands to HM-10. See list
    HM10Time <n> = set time time of a LYWSD02 only sensor to Tasmota UTC time and time zone. <n> is the sensor number in order of discovery starting with 0 (topmost sensor in the webUI list).
    HM10Auto <value> = start an automatic discovery scan with an interval of <value> seconds to receive data in BLE advertisements periodically.
    This is an active scan and it should be used only if necessary. At the moment that is the case just with MJ_HT_V1. This can change if a future HM-10 firmware starts supporting passive scan.
    NRFBeacon Set a BLE device as a beacon using the (fixed) MAC-address
    <value> (1-3 digits) = use beacon from scan list
    <value> (12 characters) = use beacon given the MAC interpreted as an uppercase string AABBCCDDEEFF
    NRFIgnore 0 = all known sensor types active_(default)_
    <value> = ignore certain sensor type (1 = Flora, 2 = MJ_HT_V1, 3 = LYWSD02, 4 = LYWSD03, 5 = CGG1, 6 = CGD1
    NRFKey Set a "bind_key" for a MAC-address to decrypt (LYWSD03MMC & MHO-C401). The argument is a 44 uppercase characters long string, which is the concatenation of the bind_key and the corresponding MAC.
    <00112233445566778899AABBCCDDEEFF> (32 characters) = bind_key
    <112233445566> (12 characters) = MAC of the sensor
    <00112233445566778899AABBCCDDEEFF112233445566> (44 characters)= final string
    NRFMjyd2s Set a "bind_key" for a MAC-address to decrypt sensor data of the MJYD2S. The argument is a 44 characters long string, which is the concatenation of the bind_key and the corresponding MAC.
    <00112233445566778899AABBCCDDEEFF> (32 characters) = bind_key
    <112233445566> (12 characters) = MAC of the sensor
    <00112233445566778899AABBCCDDEEFF112233445566> (44 characters)= final string
    NRFNlight Set the MAC of an NLIGHT
    <value> (12 characters) = MAC interpreted as an uppercase string AABBCCDDEEFF
    NRFPage Show the maximum number of sensors shown per page in the webUI list.
    <value> = set number of sensors (default = 4)
    NRFScan Scan for regular BLE-advertisements and show a list in the console
    0 = start a new scan list
    1 = append to the scan list
    2 = stop running scan

    Stepper Motors~

    Command Parameters
    MotorMIS 1,2,4,8,16 Set micro stepping increment - 1/1 (full steps) to 1/16 (default = 1)
    MotorSPR integer Set the number of steps the given motor needs for one revolution (default = 200)
    This is dependent on the type of motor and micro stepping. Most common motors are 1.8° per step.
    MotorRPM 1..300 Set revolutions per minute (default = 30)
    MotorMove integer Move the motor the given number of steps (positive values: clockwise, negative values: counterclockwise)
    MotorRotate integer Rotate the motor the given number of degrees (positive values: clockwise, negative values: counterclockwise)
    MotorTurn float Spin the motor the given number of turns (positive values: clockwise, negative values: counterclockwise)

    MP3 Player~

    The MP3 Player driver is based on the one from DFRobot. They named it DFPlayer mini. All MP3 Players with the identical Serial Control Command structure can be used.

    Note

    Player module pin RX should be connected to a GPIO defined as "MP3 Player" from the drop-down list. The driver uses a Software Serial and do not requires usage of hardware TX/RX pins.

    Command Parameters
    MP3DAC 0 = DAC on (default)
    1 = DAC off
    MP3Device Specify playback device
    1 = USB
    2 = SD Card (default (also defaults on reset or power cycle))
    MP3EQ Set equalizer mode:
    0 = normal
    1 = pop
    2 = rock
    3 = jazz
    4 = classic
    5 = bass)
    MP3Pause Pause
    MP3Play Play, works as a normal play on a real MP3 Player, starts at first MP3 file
    MP3Reset Reset the MP3 player to defaults
    MP3Stop Stop
    MP3Track x = play track <x>
    MP3Volume 0..100 = set Volume

    Thermostat~

    Command Parameters
    ThermostatModeSet<x> Sets the thermostat mode
    0 = Thermostat Off (controller inactive, default)
    1 = Thermostat in automatic mode (controller active)
    2 = Thermostat in manual mode (output switch follows the input switch, used to follow an existing wall thermostat)
    ClimateModeSet<x> Sets the climate mode
    0 = Heating mode (default)
    1 = Cooling mode
    ControllerModeSet<x> Sets the controller mode (used for thermostat in automatic mode)
    0 = Hybrid controller (Predictive ramp-up controller and PI, default)
    1 = PI controller
    2 = Predictive ramp-up controller
    TempFrostProtectSet<x> Sets the frost protection temperature. The controller, if in automatic mode, will never allow the temperature to sink below this value
    <0..12> = Temperature value in degrees Celsius/Fahrenheit (default 4.0° Celsius)
    InputSwitchSet<x> Sets the number of the input used in case in manual control
    <1..4> = Number of the input (default 1)
    InputSwitchUse<x> Switch to decide if the input shall be used to automatically switch to manual mode and assign it to the output (useful if using a serially connected wall thermostat)
    0 = Input not used (default)
    1 = Input used
    SensorInputSet<x> Sets the temperature sensor to be used
    0 = MQTT (default)
    1 = Local sensor (can be changed by define, default DS18B20)
    OutputRelaySet<x> Sets the output switch to be used for the thermostat
    <1..8> = Number of the output (default 1)
    EnableOutputSet<x> Enables or disables the physical output
    0 = Output disabled
    1 = Output enabled (default)
    TimeAllowRampupSet<x> Sets the minimum time in minutes since the last control action to be able to switch to the predictive ramp-up controller phase (applicable just in case of Hybrid controller, used normally in case of big deltas between the setpoint and the room temperature)
    <value> = Minutes (default 300 minutes)
    TempFormatSet<x> Sets the temperature format
    0 = Degrees Celsius (default)
    1 = Degrees Fahrenheit
    TempMeasuredSet<x> Sets the temperature measured by the sensor (for MQTT sensor mode)
    <TempFrostProtectSet..100> = Temperature (default 18.0° Celsius)
    TempTargetSet<x> Sets the target temperature for the controller (setpoint)
    <TempFrostProtectSet..100> = Temperature (default 18.0° Celsius)
    TempMeasuredGrdRead<x> Returns the calculated temperature gradient
    <value> = Temperature gradient in degrees Celsius/Fahrenheit
    StateEmergencySet<x> Sets the thermostat emergency flag
    0 = Emergency flag off (default)
    1 = Emergency flag on (thermostat switches to off state)
    TimeManualToAutoSet<x> Sets the time in manual mode after the last active input action (f.e. last action from serial connected wall thermostat) to switch to automatic mode
    0..1440 = time in minutes (default 60 minutes)
    PropBandSet<x> Sets the value of the proportional band of the PI controller
    0..20 = value in degrees Celsius (default 4 degrees Celsius)
    TimeResetSet<x> Sets the value of the reset time of the PI controller
    0..86400 = value in seconds (default 12000 seconds)
    TimePiProportRead<x> Returns the proportional part of the PI controller calculation in seconds
    value = value in seconds
    TimePiIntegrRead<x> Returns the integral part of the PI controller calculation in seconds
    value = value in seconds
    TimePiCycleSet<x> Sets the value of the cycle for the PI controller
    0..1440 = value in minutes (default 30 minutes)
    TempAntiWindupResetSet<x> Sets the value of the delta between controlled temperature and setpoint above which the integral part of the PI controller will be set to 0, in degrees Celsius/Fahrenheit
    0..10 = value in degrees (default 0.8° Celsius)
    TempHystSet<x> Sets the value of the temperature hysteresis for the PI controller, in degrees Celsius/Fahrenheit
    -10..10 = value in degrees (default 0.1° Celsius)
    TimeMaxActionSet<x> Sets the maximum duty cycle of the PI controller in minutes
    0..1440 = value in minutes (default 20 minutes)
    TimeMinActionSet<x> Sets the minimum duty cycle of the PI controller in minutes
    0..1440 = value in minutes (default 4 minutes)
    TimeSensLostSet<x> Sets the maximum time without a temperature sensor update to mark it as lost in minutes
    0..1440 = value in minutes (default 30 minutes)
    TimeMinTurnoffActionSet<x> Sets the minimum time in minutes within a cycle for the PI controller to switch off the output, below it, it will stay on
    0..1440 = value in minutes (default 3 minutes)
    TempRupDeltInSet<x> Sets the minimum delta between controlled temperature and setpoint for the controller to switch to ramp-up controller phase (applicable just in Hybrid controller mode)
    0..10 = value in degrees Celsius/Fahrenheit (default 0.4° Celsius)
    TempRupDeltOutSet<x> Sets the maximum delta between controlled temperature and setpoint for the controller to switch to the PI controller phase (applicable just in Hybrid controller mode)
    0..10 = value in degrees Celsius/Fahrenheit (default 0.2° Celsius)
    TimeRampupMaxSet<x> Sets the maximum time in minutes for the controller to stay in the ramp-up phase (applicable just in Hybrid controller mode
    0..1440 = value in minutes (default 960 minutes)
    TimeRampupCycleSet<x> Sets the value of the cycle for the ramp-up controller
    0..1440 = value in minutes (default 30 minutes)
    TempRampupPiAccErrSet<x> Sets the initial accumulated error when switching from ramp-up to the PI controller phase if the target temperature has not been reached (applicable just in Hybrid controller mode)
    0..25 = value in degrees Celsius/Fahrenheit (default 2° Celsius)
    CtrDutyCycleRead<x> Returns the duty cycle of the controller
    0..100 = value in %
    DiagnosticModeSet<x> Enables/disables the diagnostics flag
    0 = Diagnostics disabled
    1 = Diagnostics enabled (default)

    Domoticz~

    Command Parameters
    DzIdx<x> Show Domoticz Relay idx <x> (x = 1..4)
    0 = disable use of Relay idx <x> (default)
    <value> = Show Relay idx <x>
    DzKeyIdx<x> Show Domoticz Key idx <x> (x = 1..4)
    0 = disable use of Key idx <x> (default)
    <value> = Show Key idx <x> (to use enable ButtonTopic)
    DzSend<type> send values or state to Domoticz
    <index>,<value1(;value2)|state>
    DzSensorIdx<x> Show Domoticz Sensor idx <x> (x = 1..5)
    0 = disable use of Sensor idx <x> (default)
    <value> = Show Sensor idx <x>
    DzSwitchIdx<x> Show Domoticz Switch idx <x> (x = 1..4)
    0 = disable use of Switch idx <x> (default)
    <value> = Show Switch idx <x> (to use enable SwitchTopic)
    DzUpdateTimer Show current update timer value in seconds
    0 = disable sending interim Domoticz status (default)
    1..3600 = send status to Domoticz in defined intervals

    InfluxDB~

    Command Parameters
    Ifx InfluxDB state
    0 = off
    1 = on
    IfxHost <value> = set Influxdb host name or IP address
    IfxPort <value> = set Influxdb port
    IfxDatabase <value> = set Influxdb V1 and database name
    IfxUser <value> = set Influxdb V1 and userid
    IfxPassword <value> = set Influxdb V1 and password
    IfxBucket <value> = set Influxdb V2 and bucket name
    IfxOrg <value> = set Influxdb V2 and organization
    IfxSensor Set Influxdb sensor logging
    0 = off
    1 = on
    IfxToken <value> = set Influxdb V2 and token
    IfxPeriod <value>
    = 0 = use Teleperiod value as publication interval (default)
    10..3600 = set a different publication interval
    Even when IfxPeriod 0 is used, publication is not necessarily performed at the same time as the telemetry message.
    IfxRP <value> = set Influxdb retention policy (optional)
    If blank, default is used as defined by the InfluxDB service. Retention policy must exist in InfluxDB, otherwise http post will fail.

    KNX~

    Command Parameters
    KnxTx_Cmnd<x> 0 or 1 = send command using slot <x> set in KNX Menu at KNX_TX
    KnxTx_Val<x> <value> = send float value using slot <x> set in KNX Menu at KNX_TX
    KnxTx_Scene <value> = send scene number to the GA set in KNX Menu
    Knx_Enabled Status of KNX Communications
    0 = set to Disable
    1 = set to Enable
    Knx_Enhanced Status of Enhanced mode for KNX Communications
    0 = set to Disable
    1 = set to Enable
    Knx_PA KNX Physical Address
    0.0.0 = address not set
    x.x.x = set the device address (example 1.1.0)
    Knx_GA Return the amount of Group Address to Send Data/Commands configured
    Knx_GA<x> Setup Group Address to Send Data/Commands (<x> = KNX Group Address number)
    1 = return configuration of GA<x>
    <option>, <area>, <line>, <member> to set configuration of GA<x>
         <option> = see table below for OPTION list
         <area>, <line>, <member> = KNX Address to Send Data/Commands
    Knx_CB Return the amount of Group Address to Receive Data/Commands configured
    Knx_CB<x> Setup Group Address to Receive Data/Commands
    1 = return configuration of CB<x>
    <option>, <area>, <line>, <member> to set configuration of CB<x>
         <option> = see table below for OPTION list
         <area>, <line>, <member> = KNX Address to Receive Data/Commands
    OPTION OPTION
    Value

    OPTION
    OPTION
    Value
    1 Relay 1 17 TEMPERATURE
    2 Relay 2 18 HUMIDITY
    3 Relay 3 19 ENERGY_VOLTAGE
    4 Relay 4 20 ENERGY_CURRENT
    5 Relay 5 21 ENERGY_POWER
    6 Relay 6 22 ENERGY_POWERFACTOR
    7 Relay 7 23 ENERGY_DAILY
    8 Relay 8 24 ENERGY_START
    9 Button 1 25 ENERGY_TOTAL
    10 Button 2 26 KNX_SLOT1
    11 Button 3 27 KNX_SLOT2
    12 Button 4 28 KNX_SLOT3
    13 Button 5 29 KNX_SLOT4
    14 Button 6 30 KNX_SLOT5
    15 Button 7 255 EMPTY
    16 Button 8

    Range Extender~

    Command Parameters
    RgxClients List range extender clients
    RgxPort tcp|udp, gateway_port, client_mac, client_port = range extender port forwarding

    NeoPool~

    Command Parameters
    NPFiltration {<state> {speed}}
    get/set manual filtration (state = 0 or 1, speed = 1..3). Get if state is omitted, otherwise set accordingly <state>:
    • 0 - manual turn filtration pump off
    • 1 - manual turn filtration pump on
    optional speed control is possible for non-standard filtration types:
    • 1 - slow
    • 2 - medium
    • 3 - fast
    NPFiltrationmode {<mode>}
    get/set filtration mode (mode = 0..4 or 13). Get if mode is omitted, otherwise set accordingly <mode>:
    • 0 - MANUAL
      allows to turn the filtration (and all other systems that depend on it) on and off
    • 1 - AUTO
      allows filtering to be turned on and off according to the settings of the MBF_PAR_TIMER_BLOCK_FILT_INT timers.
    • 2 - HEATING
      similar to the AUTO mode, but includes setting the temperature for the heating function. This mode is activated only if the BF_PAR_HEATING_MODE register is at 1 and there is a heating relay assigned.
    • 3 - SMART
      adjusts the pump operating times depending on the temperature. This mode is activated only if the MBF_PAR_TEMPERATURE_ACTIVE register is at 1.
    • 4 - INTELLIGENT
      performs an intelligent filtration process in combination with the heating function. This mode is activated only if the MBF_PAR_HEATING_MODE register is at 1 and there is a heating relay assigned.
    • 13 - BACKWASH
      started when the backwash operation is activated.
    NPTime {<time>}
    get/set device time. Get if time is omitted, otherwise set device time accordingly <time>:
    • 0 - sync with Tasmota local time
    • 1 - sync with Tasmota utc time
    • 2..4294967295 - set time as epoch
    NPLight {<state> {delay}}
    get/set light (state = 0..4, delay = 5..100 in 1/10 sec). Get if state is omitted, otherwise set accordingly <state>:
    • 0 - manual turn light off
    • 1 - manual turn light on
    • 2 - manual toggle light
    • 3 - switch light into auto mode according MBF_PAR_TIMER_BLOCK_LIGHT_INT settings
    • 4 - select light RGB LED to next program. This is normally done by power the light on (if currently off), then power off the light for a given time (delay) and power on again. The default delay is 15 (=1.5 sec).
    NPpHMin {<ph>}
    (only available if pH module is installed)
    get/set pH lower limit (ph = 0..14)
    get current limit if is omitted, otherwise set.
    NPpHMax {<ph>}
    (only available if pH module is installed)
    get/set pH upper limit (ph = 0..14)
    get current limit if is omitted, otherwise set.
    NPpH {<ph>}
    (only available if pH module is installed)
    get/set pH upper limit (ph = 0..14)
    same as NPpHMax
    NPRedox {<setpoint>}
    (only available if redox module is installed)
    get/set redox set point in mV (setpoint = 0..100, the upper limit of the range may vary depending on the MBF_PAR_HIDRO_NOM register)
    get current set point if is omitted, otherwise set
    NPHydrolysis {<level>}
    (only available if hydrolysis/electrolysis control is present)
    get/set hydrolysis/electrolysis level in % (level = 0..100)
    get current level if is omitted, otherwise set
    NPIonization {<level>}
    (only available if ionization control is present)
    get/set ionization target production level (level = 0..x, the upper limit x of the range may vary depending on the MBF_PAR_ION_NOM register)
    get current level if is omitted, otherwise set
    NPChlorine {<setpoint>}
    (only available if free chlorine probe detector is installed)
    get/set chlorine set point in ppm (setpoint = 0..10)
    get current set point if is omitted, otherwise set
    NPControl
    Show information about system controls
    NPOnError {<repeat>}
    get/set auto-repeat Modbus read/write commands on error (repeat = 0..10). Get if repeat is omitted, otherwise set accordingly <repeat>:
    • 0 - disable auto-repeat on read/write error
    • 1..10 - repeat commands n times until ok
    NPResult {<format>}
    get/set addr/data result format for read/write commands (format = 0|1). Get if format is omitted, otherwise set accordingly <format>:
    • 0 - output decimal numbers
    • 1 - output hexadecimal strings, this is the default
    NPPHRes {<digits>}
    get/set number of digits in results for PH value (digits = 0..3).
    NPCLRes {<digits>}
    get/set number of digits in results for CL value (digits = 0..3).
    NPIONRes {<digits>}
    get/set number of digits in results for ION value (digits = 0..3).
    NPRead <addr> {<cnt>}
    read 16-bit register (addr = 0..0x060F, cnt = 1..30). cnt = 1 if omitted
    NPReadL <addr> {<cnt>}
    read 32-bit register (addr = 0..0x060F, cnt = 1..15). cnt = 1 if omitted
    NPWrite <addr> <data> {<data>...}
    write 16-bit register (addr = 0..0x060F, data = 0..0xFFFF). Use of data max 10 times
    NPWriteL <addr> <data> {<data>...}
    write 32-bit register (addr = 0..0x060F, data = 0..0xFFFFFFFF). Use of data max 10 times
    NPBit <addr> <bit> {<data>}
    read/write a 16-bit register single bit (addr = 0..0x060F, bit = 0..15, data = 0|1). Read if data is omitted, otherwise set single bit
    NPBitL <addr> <bit> {<data>}
    read/write a 32-bit register single bit (addr = 0..0x060F, bit = 0..31, data = 0|1). Read if data is omitted, otherwise set single bit
    NPEscape clears possible errors (like pump exceeded time etc.)
    NPExec take over changes without writing to EEPROM. This command is necessary e.g. on changes in Installer page (addr 0x0400..0x04EE).
    NPSave write data permanently into EEPROM.
    During the EEPROM write procedure the NeoPool device may be unresponsive to MODBUS requests, this process always takes less than 1 second.
    Since this process is limited by the number of EEPROM write cycles, it is recommend to write all necessary changes to the registers and only then execute EEPROM write process using this command.
    Note: The number of EEPROM writes for Sugar Valley NeoPool devices is guaranteed 100,000 cycles. As soon as this number is exceeded, further storage of information can no longer be guaranteed.

    ESP32~

    BLE ESP32~

    Command Parameters
    BLEAddrFilter Set BLE Address type filter.
    BLEAddrFilter = show filter level
    BLEAddrFilter n = set BLE address type filter 0..3 - default 3. Ignores BLE address types > filter value. Set 0 to ONLY see public addresses.
    BLEAlias Set Alias names for devices. A device may be referred to by its alias in subsequent commands
    BLEAlias mac=alias mac=alias ... = set one or more aliases from devices.
    BLEAlias2 = clear all aliases.
    BLEDebug Set BLE debug level.
    BLEDebug = show extra debug information
    BLEDebug0 = suppress extra debug
    BLEDetails Display details about received adverts
    BLEDetails0 = disable showing of details.
    BLEDetails1 mac/alias = show the next advert from device mac/alias
    BLEDetails2 mac/alias = show all advert from device mac
    BLEDevices Cause a list of known devices to be sent on MQTT, or Empty the list of known devices.
    BLEDevices0 = clear the known devices list.
    BLEDevices = Cause the known devices list to be published on stat/TASName/BLE.
    BLEMaxAge Set the timeout for device adverts.
    BLEMaxAge n = set the devices timeout to n seconds.
    BLEMaxAge = display the device timeout.
    BLEMode Change the operational mode of the BLE driver.
    BLEMode0 = disable regular BLE scans.
    BLEMode1 = BLE scan on command only.
    BLEMode2 = regular BLE scanning (default).
    BLEName Read or write the name of a BLE device.
    BLEName mac|alias = read the name of a device using 1800/2A00.
    BLEName mac|alias = write the name of a device using 1800/2A00 - many devices are read only.
    BLEOp Perform a simple active BLE operation (read/write/notify).
    see separate description in source code
    BLEPeriod Set the period for publish of BLE data
    <value> = set interval in seconds
    BLEScan Cause/Configure BLE a scan
    BLEScan0 0..1 = enable or disable Active scanning. (an active scan will gather extra data from devices, including name)
    BLEScan = Trigger a 20s scan now if in BLEMode1
    BLEScan n = Trigger a scan now for n seconds if in BLEMode1
    IBEACON Show or set enable for the iBeacon driver
    IBEACON = Display 0
    IBEACONclear Clear iBeacon list
    IBEACONonlyaliased Show or set OnlyAliased for the iBeacon driver
    IBEACONonlyaliased = Display 0
    IBEACONperiod Display or Set the period for publish of iBeacon data
    IBEACONperiod = display interval
    IBEACONperiod ss = set interval in seconds
    IBEACONtimeout Display or Set the timeout for iBeacon devices
    IBEACONtimeout = display timeout
    IBEACONtimeout ss = set timeout in seconds

    BLE MI Sensors~

    Command Parameters
    MI32Battery Trigger an active read of battery values.
    MI32Battery = request the driver read the battery from all sensors which have active battery read requirements.
    MI32Block Block or unblock a sensor device.
    MI32Block = list blocked devices by mac.
    MI32Block <mac or blealias> = Block one mac/alias.
    MI32Key Add a decryption key.
    MI32Key hexkey = add a 44 character decryption key to the keys list.
    MI32Keys Add one or more decryption keys by mac or alias.
    MI32Keys = list keys.
    MI32Keys <mac or blealias>=<bind_key> <mac or blealias>=<bind_key> ... = add keys for MAC or ble_alias.
    MI32Keys <mac or blealias>= - remove keys for one mac
    MI32Option<x> n Set driver options at runtime
    x=0 - 0 -> sends only recently received sensor data, 1 -> aggregates all recent sensors data types
    x=1 - 0 -> shows full sensor data at TELEPERIOD, 1 -> disable sensor data at TELEPERIOD
    x=2 - 0 -> sensor data only at TELEPERIOD (default and "usual" Tasmota style), 1 -> direct bridging of BLE-data to mqtt-messages
    x=4 - 0 -> always display battery info, 1 -> disable "bogus" battery info from LYWSD03MMC and MHOC401
    x=5 - 0 -> show all relevant BLE sensors, 1 -> show only sensors with a BLEAlias
    x=6 - 0 -> normal sensor message, 1 -> publish on tele/<mi32topic>/<name> 1 sensor on flat JSON (see Mi32Topic), 2 -> same as 1 with sensor name key
    MI32Page Display/Set the sensors per page in the web view.
    MI32page = show sensors per page.
    MI32page n = Set sensors per page to n.
    MI32Period Display/Set the active scan and tele period for the MI32 driver.
    MI32Period = display the period in seconds.
    MI32Period n = Set the MI driver active read and tele period to n seconds.
    MI32Time <x> = set the time on the device in slot x.
    MI32Topic Topic to be used with Mi32Option6 > 0 (default to tasmota_ble)
    MI32Unit <x> = set the current Tasmota temperature unit as the temp unit for sensor in slot x.

    Camera~

    Command Parameters
    Wc Query all camera settings
    WcAEC 1 = enable auto exposure control (sensor), 0 = disable (default)
    WcAECDSP 1 = enable auto exposure control (DSP), 0 = disable (default)
    WcAECValue 0..1024 = setauto exposure control value
    WcAELevel -2..+2 = set auto exposure control level
    WcAGC 1 = enable auto gain control, 0 = disable (default)
    WcAGCGain 0..30 = set auto gain control gain value
    WcAWB 1 = enable Auto White Balance, 0 = disable (default)
    WcAWBGain 1 = enable Auto White Balance Gain, 0 = disable (default)
    WcBPC 1 = enable Black Pixel Correct, 0 = disable (default)
    WcBrightness -2..+2 = set picture brightness
    WcClock 10..200 = set clock speed in MHz (default = 20)
    WcColorbar 1 = show colorbar, 0 = hide colorbar (default)
    WcContrast -2..+2 = set picture contrast
    WcDCW 1 = enable downscale, 0 = disable (default)
    WcFeature Extended features. Only boards with PSRAM should be used.
    0 = off, 1 = reduce FPS and increase exposure time for better low light performance, 2 = nightmode, increase exposure time and lower the Framerate depending on available light
    WcFlip 1 = enable flip camera image,0 = disable (default)
    WcGainCeiling 0 .. 6 = set gain ceiling (0 = x2, 1 = x4, 2 = x8, 3 = x16, 4 = x32, 5 = x64, 6 = x128)
    WcGammaCorrect 1 = enable auto gamma correct, 0 = disable (default)
    WcInit Initialize camera
    WcLensCorrect1 = enable auto lens correct, 0 = disable (default)
    WcMirror 1 = enable mirror camera image, 0 = disable (default)
    WcResolution Set camera resolution.
    0 = 96x96 (96x96)
    1 = QQVGA2 (128x160)
    2 = QCIF (176x144)
    3 = HQVGA (240x176)
    4 = QVGA (320x240)
    5 = CIF (400x296)
    6 = VGA (640x480)
    7 = SVGA (800x600)
    8 = XGA (1024x768)
    9 = SXGA (1280x1024)
    10 = UXGA (1600x1200)
    WcRtsp (Requires defined ENABLE_RTSPSERVER)
    1 = start RTSP server (forces restart) , 0 = stop server
    WcSaturation -2..+2 = set picture saturation
    WcSpecialEffect Set picture effect: 0 = off, 1 = Negative, 2 = Grayscale, 3 = Red Tint, 4 = Green Tint, 5 = Blue Tint, 6 = Sepia
    WcStats show statistics
    WcStream 1 = start webcam stream at http://<device_ip>:81/stream or http://<device_ip>:81/cam.mjpeg
    0 = stop stream
    WcWBMode White Balance Mode
    0 = auto, 1 = Sunny, 2 = Cloudy, 3 = Office, 4 = Home
    WcWPC 1 = enable White Pixel Correct, 0 = disable (default)

    Ethernet~

    Command Parameters
    Ethernet Only for ESP32 boards with additional LAN chip
    0 = disable Ethernet
    1 = enable Ethernet (default)
    EthAddress 0..31 = PHYxx address
    EthClockMode Ethernet clock mode.
    0 = ETH_CLOCK_GPIO0_IN
    1 = ETH_CLOCK_GPIO0_OUT
    2 = ETH_CLOCK_GPIO16_OUT
    3 = ETH_CLOCK_GPIO17_OUT (default)
    EthType Ethernet type.
    0 = ETH_PHY_LAN8720 (default)
    1 = ETH_PHY_TLK110
    2 = ETH_PHY_RTL8201
    3 = ETH_PHY_DP83848
    4 = ETH_PHY_DM9051
    5= ETH_PHY_KSZ8041
    6 = ETH_PHY_KSZ8081
    7= ETH_PHY_JL1101
    EthIPAddress Set device Ethernet IP address (for Wi-Fi, see IpAddress)
  • 0.0.0.0 = use dynamic IP address (DHCP)
  • XXX.XXX.XXX.XXX = set static IP address

  • Follow any IP configuration commands with restart 1 to apply changes
    EthGateway Set gateway IP address
    EthSubnetmask Set subnet mask
    EthDNSServer1
    EthDNSServer2
    Set DNS servers IP address
    See Also wifi - Enable/Disable Wi-Fi
    \ No newline at end of file diff --git a/Compile-your-build/index.html b/Compile-your-build/index.html new file mode 100644 index 0000000000..aff675db30 --- /dev/null +++ b/Compile-your-build/index.html @@ -0,0 +1,143 @@ + Compiling - Tasmota
    Skip to content

    Compiling

    Flash and memory space on an ESP82XX chip is limited and very valuable. Because of that our precompiled binaries include the most popular features of Tasmota but no build can include all of them.

    To include a feature you need (or build completely customized Tasmota) you will have to configure and compile your own version.

    First you will need Tasmota's source code (either development or master branch) and a compiling tool.

    Compiling Tools~

    If you want to modify the code or default settings you can use:

    Online Compilers~

    _Can only create a firmware binary. Use one of the tools to flash it to your device or try the Webserial ESPTool.

    • Gitpod - compile your own binary in the cloud using Gitpod.
    • TasmoCompiler - simple web GUI to compile Tasmota with your own settings

    Simplest way to compile is with GitPod, requires only a web browser.

    Once you have set up the development environment, unzip the source code into a folder.

    Customize Your Build~

    There are mainly 2 type of possible customization:

    1. Changing default settings that will be used by Tasmota when running for the first time on a blank device (no previous existing configutrion in flash or flash erased). This can be done on any variant as it doesn't change the code base, memory footprint or required libraries. Such customization include: default Wi-Fi settings, default MQTT settings, default values for a setting including SetOption<x>.

    2. Adding or removing features. This is essentially supported only on the base tasmota/tasmota32 environment). Other variants have been fine tuned and trying to add/remove features to them is most likely to fail and Tasmota development team will provide no support. The typical failure is trying to add sensors to tasmota-display or adding displays to tasmota-sensors. The proper way is to add both sensors and displays to tasmota.

    Do not try to add or remove features to a variant, only to tasmota or tasmota32

    General Customization Principle~

    Create a new file in /tasmota folder called user_config_override.h. You can copy the sample file user_config_override_sample.h that is already there and which include some sample definition for coding your own Wifi SSID and password inside the Tasmota firmware.

    Open the file in chosen development environment for editing.

    Do not modify my_user_config.h

    It is strongly recommended to NOT customize your build by making changes in my_user_config.h because the changes you made there will be overwritten if you download/clone a newer version of Tasmota code-base. At least this would make any merge complicated. Add your custom configurations ONLY in user_config_override.h. The file my_user_config.h is a great reference for available settings and features.

    A good way to avoid dealing with the source code files is to pass defines as a flag in platformio_tasmota_cenv.ini. This needs a special stringification with escape double quotes (" -> \\") in the constant value and wrapping a line in single quotes.

    build_flags                 = ${env:tasmota32_base.build_flags}
    +                              -DOTA_URL='""'
    +                              '-DUSER_BACKLOG="so11 1; br load(\\"setup.be\\")"'
    +

    Changing Default Settings~

    Most default settings are defined in my_user_config.h along with an explanation and the command used to change it dynamically. For example:

    #define WIFI_CONFIG_TOOL       WIFI_RETRY        // [WifiConfig] Default tool if Wi-Fi fails to connect (default option: 4 - WIFI_RETRY)
    +                                                 // (WIFI_RESTART, WIFI_MANAGER, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY)
    +                                                 // The configuration can be changed after first setup using WifiConfig 0, 2, 4, 5, 6 and 7.
    +#define WIFI_SCAN_AT_RESTART   false             // [SetOption56] Scan Wi-Fi network at restart for configured AP's
    +
    The first line shows that WIFI_CONFIG_TOOL is the macro matching the command WifiConfig. The default value, as stated in WifiConfig's documentation is WIFI_RETRY (value 4).

    The other line shows the default value for SetOption56 which is by default false (OFFor 0).

    If you want to override any of these in your own binary, add the following in user_config_override.h:

    #ifdef %identifier%
    +#undef %identifier%
    +#endif
    +#define %identifier%   %the_new_value%
    +
    Example:
    #ifdef WIFI_CONFIG_TOOL
    +#undef WIFI_CONFIG_TOOL
    +#endif
    +#define WIFI_CONFIG_TOOL  WIFI_WAIT   // Change WifiConfig to wait (5)
    +

    Enabling a Feature in tasmota~

    A feature can be enabled by #defining the matching USE_featurename macro. It can be disabled by #undefining the same macro. All features and their identifier can be found in my_user_config.h.

    Best practice to enable a feature is to use:

    #ifndef %identifier%
    +#define %identifier%
    +#endif
    +

    Best practice to disable a feature is to use:

    #ifdef %identifier%
    +#undef %identifier%
    +#endif
    +

    If the feature you want to customize have a value like for example: #define WIFI_CONFIG_TOOL WIFI_WAIT, the best practice to modify it is to use:

    #ifdef %identifier%
    +#undef %identifier%
    +#endif
    +#define %identifier% %value%
    +
    Directives Description
    #define %identifier% enables the feature
    #undef %identifier% disables the feature
    #ifdef %identifier% checks if the feature is defined in code
    #ifndef %identifier% checks if the feature is not defined
    #endif closes #if statement

    Enable blinds and shutters support

    #ifndef USE_SHUTTER
    +#define USE_SHUTTER             // Add Shutter support for up to 4 shutter with different motortypes (+6k code)
    +#endif
    +

    identifier = USE_SHUTTER

    1. check whether USE_SHUTTER is already defined and proceed if it is not
    2. this line copied from my_user_config.h then uncommented, tells the compiler to include (#define) shutter support
    3. close the IF statement from line 1

    Disable Domoticz support

    #ifdef USE_DOMOTICZ
    +#undef USE_DOMOTICZ                              
    +#endif 
    +

    identifier = USE_DOMOTICZ

    1. check whether USE_DOMOTICZ is already defined and proceed if it is
    2. tell the compiler to remove (#undef) Domoticz support
    3. close the IF statement from line 1

    It is not recommended to change my_user_config.h, use it only for reference

    Save file, compile the custom binary and flash it

    Note

    There are limits to how many features can be included! If you go overboard code might not compile due to features conflicting or might not be able to be flashed if it exceeds ESP8266 limits.

    Advanced Customization~

    USER_BACKLOG~

    USER_BACKLOG allows a set of commands to be automatically executed when the binary is ran for the first time on blank device (no settings in flash) or after a settings reset using reset 1/reset 2. It should be defined as a list of commands separated by a ;. No Backlog command is required. It can be used for example for settings which do not have a changeable default. An interesting usage is to automatically reconfigure a device from a saved configuration file right after a reset 1/reset 2.

    Automatically load a configuration backup (*.dmp) file based on the MAC address of the device

    #define USER_BACKLOG "WebGetConfig http://myserver/tasmota/conf/%id%.dmp"
    +

    USER_RULE~

    If you need some rules to be automatically populated in your binary, you can define USER_RULE<x>.

    #define USER_RULE1 "On Switch1#state DO publish cmnd/otherdevice/POWER %value% ENDON"
    +

    Defining Multiple Custom Firmwares~

    You may want to generate multiple custom firmwares such as one for switches/relays, one for sensors, in a similar way as Tasmota provides different binaries. This can be achieved very simply.

    1. Open the file platformio_tasmota_cenv.ini. You can define your own binaries here. cenv stands for Custom ENVironment where an environment is a specific binary to generate.
    2. In your user_config_override.h you can create sections with specific settings for each type of firmware. SSID and MQTT can be outside of the section so they apply to every binary.

    Sample platformio_tasmota_cenv.ini~

    ; *********************************************************************
    +[platformio]
    +; For best Gitpod performance remove the ";" in the next line. Needed 
    +; Platformio files are cached and installed at first run
    +;core_dir = .platformio
    +
    +; *** Build/upload environment
    +default_envs =
    +; *** Uncomment the line(s) below to select version(s) that will be build
    +;     by default. Commented versions can still be build individually from
    +;     VSCode or command line
    +                tasmota-foo
    +                tasmota-bar
    +                tasmota32-foo
    +                tasmota32-grizzly
    +
    +; *********************************************************************
    +; Common section can override global parameters for all builds
    +[common]
    +
    +; *** Upload Serial reset method for Wemos and NodeMCU
    +upload_port               = COM4
    +
    +; *********************************************************************
    +; This section show how to create 2 alternative binaries : tasmota-foo.bin
    +; and tasmota-bar.bin. Those binaries are derived form tasmota.bin and 
    +; customization is defined in user_config_override.h 
    +; Those binaries are for ESP8266
    +; The name after the env: tag will give its name to the binary
    +[env:tasmota-foo]
    +build_flags = ${env.build_flags} -DFIRMWARE_FOO
    +
    +[env:tasmota-bar]
    +build_flags = ${env.build_flags} -DFIRMWARE_BAR
    +
    +; *********************************************************************
    +; Similar example for ESP32
    +; Note that you must explicitly state that they derive from `tasmota32`
    +[env:tasmota32-foo]
    +extends = env:tasmota32_base
    +build_flags             = ${env:tasmota32_base.build_flags} -DFIRMWARE_FOO
    +
    +[env:tasmota32-grizzly]
    +extends = env:tasmota32_base
    +build_flags             = ${env:tasmota32_base.build_flags} -DFIRMWARE_GRIZZLY
    +

    Sample user_config_override.h~

    #ifndef _USER_CONFIG_OVERRIDE_H_
    +#define _USER_CONFIG_OVERRIDE_H_
    +
    +// force the compiler to show a warning to confirm that this file is included
    +#warning **** user_config_override.h: Using Settings from this File ****
    +
    +// ***********************************************
    +// ** Global settings for all binaries ***********
    +
    +// -- Setup your own Wifi settings  ---------------
    +#undef  STA_SSID1
    +#define STA_SSID1         "YourSSID"             // [Ssid1] Wifi SSID
    +#undef  STA_PASS1
    +#define STA_PASS1         "YourWifiPassword"     // [Password1] Wifi password
    +
    +// You can also define your IP settings or your MQTT settings
    +
    +// ***********************************************
    +// ** Firmware specific settings *****************
    +
    +// -- Options for firmware tasmota-foo and tasmota32-foo ------
    +#ifdef FIRMWARE_FOO
    +    // This line will issue a warning during the build (yellow in 
    +    // VSCode) so you see which section is used
    +    #warning **** Build: FOO ****
    +    // -- CODE_IMAGE_STR is the name shown between brackets on the 
    +    //    Information page or in INFO MQTT messages
    +    #undef CODE_IMAGE_STR
    +    #define CODE_IMAGE_STR "foo"
    +
    +    // Put here your override for firmware tasmota-foo
    +    #define USE_I2C
    +    #define USE_SENSOR_FOO  // Beware this doesn't exist !!!
    +
    +#endif
    +
    +// -- Options for firmware tasmota-bar ------
    +#ifdef FIRMWARE_BAR
    +    #warning **** Build: BAR ****
    +    #undef CODE_IMAGE_STR
    +    #define CODE_IMAGE_STR "bar"
    +
    +    // Put here your override for firmware tasmota-bar
    +
    +#endif
    +
    +// -- Options for firmware tasmota32-grizzly ------
    +#ifdef FIRMWARE_GRIZZLY
    +
    +    // If these settings are only for ESP32, you can check these
    +    // are used only when building for ESP32
    +    #ifndef ESP32
    +    #error *** This setup of for tasmota32 only ***
    +    #endif
    +
    +    #warning **** Build: GRIZZLY ****
    +    #undef CODE_IMAGE_STR
    +    #define CODE_IMAGE_STR "grizzly"
    +
    +    // Put here your override for firmware tasmota32-grizzly
    +
    +#endif
    +
    +#endif  // _USER_CONFIG_OVERRIDE_H_
    +
    \ No newline at end of file diff --git a/Components-old/index.html b/Components-old/index.html new file mode 100644 index 0000000000..4b9a29d89b --- /dev/null +++ b/Components-old/index.html @@ -0,0 +1 @@ + Components old - Tasmota
    Skip to content

    Components old

    Component is anything wired to the ESP8266/ESP8255 chip to be controlled by or send data to it.

    Components can be: buttons, switches, relays, LEDs, sensors, displays, MCU units, etc. Every component is assigned in the device template to the GPIO it is wired (connected) to.

    Every Tasmota device has some components configured by default. Most often there is a relay, a button and a LED configured as is the case for a Sonoff Basic in the following image.

    Bug

    Tasmota 9.1 completely redesigned GPIO mapping to allow for future expansion. Read more about the GPIO Conversion

    Assigning Components~

    If you wish to expand a device with a peripheral component, after properly wiring everything, you need to assign it to a free GPIO in Configure Module page or use command GPIO<x>.

    Read more about peripherals.

    Tip

    GPIOs configured as User (1) are the GPIOs that can be assigned to components in the Configure Module page.

    GPIO Conversion~

    Old GPIO New GPIO Name Description
    255 1 User User
    0 0 None Not used
    1 1184 DHT11 DHT11 sensor
    2 1216 AM2301 AM230X, DHT21 and DHT22 sensor
    3 1248 SI7021 Only for Sonoff Si7021, not the i2c version
    4 1312 DS18x20 Dallas Semiconductor DS18b20 1-Wire temperature sensor
    5 608 I2C SCL I2C serial clock pin, used with any I2C component (sensors, displays, ...)
    6 640 I2C SDA I2C serial data pin, used with any I2C component (sensors, displays, ...)
    7 1376 WS2812 Addressable LEDs such as WS281X or Neopixel
    8 1056 IRsend IR Transmitter LED
    9 160 Switch 1 Switch
    10 161 Switch 2 Switch
    11 162 Switch 3 Switch
    12 163 Switch 4 Switch
    13 164 Switch 5 Switch
    14 165 Switch 6 Switch
    15 166 Switch 7 Switch
    16 167 Switch 8 Switch
    17 32 Button 1 Button active low, internal pull-up resistor
    18 33 Button 2 Button active low, internal pull-up resistor
    19 34 Button 3 Button active low, internal pull-up resistor
    20 35 Button 4 Button active low, internal pull-up resistor
    21 224 Relay 1 Relay
    22 225 Relay 2 Relay
    23 226 Relay 3 Relay
    24 227 Relay 4 Relay
    25 228 Relay 5 Relay
    26 229 Relay 6 Relay
    27 230 Relay 7 Relay
    28 231 Relay 8 Relay
    29 256 Relay_i 1 Relay inverted
    30 257 Relay_i 2 Relay inverted
    31 258 Relay_i 3 Relay inverted
    32 259 Relay_i 4 Relay inverted
    33 260 Relay_i 5 Relay inverted
    34 261 Relay_i 6 Relay inverted
    35 262 Relay_i 7 Relay inverted
    36 263 Relay_i 8 Relay inverted
    37 416 PWM 1 Pulse Width Modulated Output
    38 417 PWM 2 Pulse Width Modulated Output
    39 418 PWM 3 Pulse Width Modulated Output
    40 419 PWM 4 Pulse Width Modulated Output
    41 420 PWM 5 Pulse Width Modulated Output
    42 352 Counter 1 Counter Input
    43 353 Counter 2 Counter Input
    44 354 Counter 3 Counter Input
    45 355 Counter 4 Counter Input
    46 448 PWM_i 1 Pulse Width Modulated inverted Output
    47 449 PWM_i 2 Pulse Width Modulated inverted Output
    48 450 PWM_i 3 Pulse Width Modulated inverted Output
    49 451 PWM_i 4 Pulse Width Modulated inverted Output
    50 452 PWM_i 5 Pulse Width Modulated inverted Output
    51 1088 IRrecv IR Receiver Input (for example TSOP1838)
    52 288 Led 1 LED
    53 289 Led 2 LED
    54 290 Led 3 LED
    55 291 Led 4 LED
    56 320 Led_i 1 Inverted LED - default state ON
    57 321 Led_i 2 Inverted LED - default state ON
    58 322 Led_i 3 Inverted LED - default state ON
    59 323 Led_i 4 Inverted LED - default state ON
    60 1408 MHZ Rx MHZ 19 CO2 Sensor
    61 1440 MHZ Tx MHZ 19 CO2 Sensor
    62 1472 PZEM0XX Tx Peacefair Pzem-0XX Power Meter Tx pin
    63 1504 PZEM004 Rx Peacefair Pzem-004 Power Meter Rx pin
    64 1600 SAir Rx Sensor Senseair
    65 1632 SAir Tx Sensor Senseair
    66 768 SPI CS SPI Interface (ePaper Display)
    67 800 SPI DC SPI Interface (ePaper Display)
    68 992 BkLight Backlight (Display)
    69 1696 PMS5003 PMS5003 Air Quality Sensor
    70 1760 SDS0X1 Rx Nova Fitness SDS011 Laser Dust Sensor Rx pin
    71 1792 SerBr Rx Serial Bridge Receive
    72 1824 SerBr Tx Serial Bridge Transmit
    73 1856 SR04 Tri Ultrasonic Sensor HC-SR04 Trigger pin
    74 1888 SR04 Ech Ultrasonic Sensor HC-SR04 Echo pin
    75 1920 SDMx20 Tx SDMx20-Modbus Multifunction Power Analyser Tx pin
    76 1952 SDMx20 Rx SDMx20-Modbus Multifunction Power Analyser Rx pin
    77 1984 SDM630 Tx SDM630-Modbus Multifunction Power Analyser Tx pin
    78 2016 SDM630 Rx SDM630-Modbus Multifunction Power Analyser Rx pin
    79 2048 TM16 CLK TM1638 Switch Module
    80 2080 TM16 DIO TM1638 Switch Module
    81 2112 TM16 STB TM1638 Switch Module
    82 192 Switch_n 1 Switch, no pull-up resistor
    83 193 Switch_n 2 Switch, no pull-up resistor
    84 194 Switch_n 3 Switch, no pull-up resistor
    85 195 Switch_n 4 Switch, no pull-up resistor
    86 196 Switch_n 5 Switch, no pull-up resistor
    87 197 Switch_n 6 Switch, no pull-up resistor
    88 198 Switch_n 7 Switch, no pull-up resistor
    89 199 Switch_n 8 Switch, no pull-up resistor
    90 64 Button_n 1 Button, active low, no internal pull-up resistor
    91 65 Button_n 2 Button, active low, no internal pull-up resistor
    92 66 Button_n 3 Button, active low, no internal pull-up resistor
    93 67 Button_n 4 Button, active low, no internal pull-up resistor
    94 384 Counter_n 1 Counter sensor, no pull-up resistor
    95 385 Counter_n 2 Counter sensor, no pull-up resistor
    96 386 Counter_n 3 Counter sensor, no pull-up resistor
    97 387 Counter_n 4 Counter sensor, no pull-up resistor
    98 1536 PZEM016 Rx Peacefair Pzem-016 Power Meter Rx pin
    99 1568 PZEM017 Rx Peacefair Pzem-017 Power Meter Rx pin
    100 2144 MP3 Player DF MP3 Player mini (Input)
    101 1728 SDS0X1 Tx Nova Fitness SDS011 Laser Dust Sensor Tx pin
    102 2176 HX711 SCK HX711 weight sensor serial clock input
    103 2208 HX711 DAT HX711 weight sensor data output
    104 2240 TX20 TX20 Wind Sensor Input (Tx from sensor)
    105 1120 RFSend RF Emitter (433Mhz module needed; Requires self-compile with RF_SENSOR and USE_RC_SWITCH)
    106 1152 RFrecv RF Receiver (433Mhz module needed; Requires self-compile with RF_SENSOR and USE_RC_SWITCH)
    107 2272 Tuya Tx Tuya Transfer pin
    108 2304 Tuya Rx Tuya Receive pin
    109 2336 MGC3130 Xfr MGC3130 E-field Xfr pin
    110 2368 MGC3130 Rst MGC3130 E-field Reset pin
    111 832 SSPI MISO Software SPI MISO (Display)
    112 864 SSPI MOSI Software SPI MOSI (Display)
    113 896 SSPI SCLK Software SPI SCLK (Display)
    114 928 SSPI CS Software SPI CS (Display)
    115 960 SSPI DC Software SPI DC (Display)
    116 2400 RF Sensor Theo Arendst RF433 Sensor
    117 2432 AZ Rx AZ 7798 CO2 datalogger
    118 2464 AZ Tx AZ 7798 CO2 datalogger
    119 2496 MX31855 CS MAX31855 Thermocouple Sensor Chip Select pin
    120 2528 MX31855 CLK MAX31855 Thermocouple Sensor Serial Clock pin
    121 2560 MX31855 DO MAX31855 Thermocouple Sensor Digital Output pin
    122 96 Button_i 1 Button inverted, active high with internal pull-up resistor
    123 97 Button_i 2 Button inverted, active high with internal pull-up resistor
    124 98 Button_i 3 Button inverted, active high with internal pull-up resistor
    125 99 Button_i 4 Button inverted, active high with internal pull-up resistor
    126 128 Button_in 1 Button inverted, active high no internal pull-up resistor
    127 129 Button_in 2 Button inverted, active high no internal pull-up resistor
    128 130 Button_in 3 Button inverted, active high no internal pull-up resistor
    129 131 Button_in 4 Button inverted, active high no internal pull-up resistor
    130 2592 HLWBL SEL Energy Monitoring (for example Pow)
    131 2624 HLWBL SELi Energy Monitoring (for example Pow)
    132 2656 HLWBL CF1 Energy Monitoring (for example Pow)
    133 2688 HLW8012 CF HLW8012 Single Phase Energy Monitor Chip CF pin
    134 2720 BL0937 CF BL0937 Single Phase Energy Monitor Chip CF pin
    135 2752 MCP39F5 Tx Energy Monitoring (for example Shelly2)
    136 2784 MCP39F5 Rx Energy Monitoring (for example Shelly2)
    137 2816 MCP39F5 Rst Energy Monitoring (for example Shelly2)
    138 2848 PN532 Tx PN532 RFID/NFC Reader Tx pin
    139 2880 PN532 Rx PN532 RFID/NFC Reader Rx pin
    140 2912 SM16716 CLK SM16716 Pixel LED Serial Clock pin
    141 2944 SM16716 DAT SM16716 Pixel LED Data pin
    142 2976 SM16716 PWR SM16716 Pixel LED Power pin
    143 3008 MY92x1 DI Light Bulb with MY92x controller
    144 3040 MY92x1 DCKI Light Bulb with MY92x controller
    145 3072 CSE7766 Tx CSE7766 Single Phase Energy Monitor Chip Tx pin
    146 3104 CSE7766 Rx CSE7766 Single Phase Energy Monitor Chip Rx pin
    147 3136 ALux IrRcv AriLux RGB Controller IR receive (Input)
    148 3200 Serial Tx Serial Transfer pin
    149 3232 Serial Rx Serial Receive pin
    150 3264 Rotary A 1 Rotary Encoder (Mi Desk Lamp)
    151 3296 Rotary B 1 Rotary Encoder (Mi Desk Lamp)
    152 3265 Rotary A 2 Rotary Encoder (Mi Desk Lamp)
    153 3297 Rotary B 2 Rotary Encoder (Mi Desk Lamp)
    154 3392 HRE CLOCK Clock/Power line for HR-E Water Meter
    155 3424 HRE DATA Data line for HR-E Water Meter
    156 3456 ADE7953_IRQ ADE7953 IRQ
    157 544 LedLink Device Status LED
    158 576 LedLinki Device Status LED, inverted
    159 3168 ALux IrSel For AriLux devices - switches between IR/RF mode
    160 480 Buzzer Sonoff iFan03 Buzzer
    161 512 Buzzeri Sonoff iFan03 Buzzer inverted
    162 1024 OLED Reset OLED Display Reset
    163 3488 SolaxX1 Tx Solax Inverter Tx pin
    164 3520 SolaxX1 Rx Solax Inverter Rx pin
    165 3552 Zigbee Tx Zigbee Serial interface Tx
    166 3584 Zigbee Rx Zigbee Serial interface Rx
    167 3616 RDM6300 Rx RDM6300 RX
    168 3648 iBeacon Tx HM17 iBeacon Tx
    169 3680 iBeacon Rx HM17 iBeacon Rx
    170 3712 A4988 DIR A4988 Motor Direction
    171 3744 A4988 STP A4988 Step motor
    172 3776 A4988 ENA A4988 Enable motor
    173 3808 A4988 MS1 A4988 Microstep increment select pin1
    174 3809 A4988 MS2 A4988 Microstep increment select pin2
    175 3810 A4988 MS3 A4988 Microstep increment select pin3
    176 3904 DDS238-2 Tx DDS2382 Serial interface Tx
    177 3936 DDS238-2 Rx DDS2382 Serial interface Rx
    178 3968 DDSU666 Tx DDSU666 Serial interface Tx
    179 4000 DDSU666 Rx DDSU666 Serial interface Rx
    180 4032 SM2135 CLK SM2135 Clk
    181 4064 SM2135 DAT SM2135 Dat
    182 4096 DeepSleep DeepSleep wake switch
    183 4128 EXS Enable EXS Dimmer MCU Enable
    184 4160 Client TX TasmotaClient TX
    185 4192 Client RX TasmotaClient RX
    186 4224 Client RST TasmotaClient Reset Pin
    187 4256 Client RSTi TasmotaClient Reset Inverted
    188 4288 HPMA RX Honeywell HPMA115S0 Serial Rx
    189 4320 HPMA TX Honeywell HPMA115S0 Serial Tx
    190 4352 GPS RX GPS Serial Tx
    191 4384 GPS TX GPS Serial Tx
    192 1344 DSB OUT Pseudo Single wire DS18B20 or DS18S20
    193 1280 DHT11 OUT Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321
    194 4416 HM10 RX HM10-BLE-Mijia-bridge Serial
    195 4448 HM10 TX HM10-BLE-Mijia-bridge Serial
    196 4480 LE01MR RX F&F LE-01MR Energy Meter Serial
    197 4512 LE01MR TX F&F LE-01MR Energy Meter Serial
    198 4544 CC1101 GDO0 CC1101 RX Pin
    199 4576 CC1101 GDO2 CC1101 TX Pin
    200 4608 HRXL RX Data from MaxBotix HRXL sonar range sensor
    201 4640 MOODL TX ElectriQ iQ-wifiMOODL Serial TX
    202 4672 AS3935 Lightning Detector Interrupt Pin
    203 1664 PMS5003 TX Plantower PMS5003 Serial interface
    204 4928 Boiler OT Rx OpenTherm Boiler RX pin
    205 4960 Boiler OT Tx OpenTherm Boiler TX pin
    206 4992 Windmeter Speed WindMeter speed counter pin
    207 5056 BL0940 RX BL0940 serial interface
    208 5088 TCP TX TCP Serial bridge
    209 5120 TCP RX TCP Serial bridge
    210 5152 TELEINFO RX Teleinfo serial interface
    211 5184 TELEINFO Enable Teleinfo Enable PIN
    212 5216 LMT01 LMT01 input counting pin
    213 5248 IEM3000 TX IEM3000 Serial interface
    214 5280 IEM3000 RX IEM3000 Serial interface
    215 5312 Zigbee RST Zigbee reset
    216 5344 DYP Rx DYP-ME007 Rx pin
    5728 Option a 1 Enable PWM2 high impedance if powered off as used by Wyze bulbs
    5729 Option a 2 Enable dummy energy monitor
    5730 Option a 3 Enable UDisplay universal display driver
    Rotary A_n Rotary Encoder
    Rotary B_n Rotary Encoder
    Button_d 1 Button, active low with internal pull-down resistor
    Button_d 2 Button, active low with internal pull-down resistor
    Button_d 3 Button, active low with internal pull-down resistor
    Button_d 4 Button, active low with internal pull-down resistor
    Button_id 1 Button inverted, active high with internal pull-down resistor
    Button_id 2 Button inverted, active high with internal pull-down resistor
    Button_id 3 Button inverted, active high with internal pull-down resistor
    Button_id 4 Button inverted, active high with internal pull-down resistor
    Switch_d 1 Switch, with pull-down resistor
    Switch_d 2 Switch, with pull-down resistor
    Switch_d 3 Switch, with pull-down resistor
    Switch_d 4 Switch, with pull-down resistor
    Switch_d 5 Switch, with pull-down resistor
    Switch_d 6 Switch, with pull-down resistor
    Switch_d 7 Switch, with pull-down resistor
    Switch_d 8 Switch, with pull-down resistor

    ADC Conversion~

    Old ADC New ADC Option WebUI display MQTT message
    0 0 None none none
    1 4704 Analog Analog0 %value% {"A0":%value%}
    2 4736 Temperature Temperature %value% °C (°F) {"Temperature":%value%},"TempUnit":"C"}
    3 4768 Light Illuminance %value% lux {"Illuminance":%value%}
    4 4800 Button none none
    5 4832 Buttoni none none
    6 4864 Range Range %value% {"Range":%value%}
    7 4896 CT Power Voltage 230 V Current %value A Power %value W Energy Total %value kWh {"Energy":%value,"Power":%value,"Voltage":230,"Current":%value}
    8 3328 Joystick none {"ANALOG":{"Joy1":%value%}
    \ No newline at end of file diff --git a/Components/index.html b/Components/index.html new file mode 100644 index 0000000000..ebd6af42b0 --- /dev/null +++ b/Components/index.html @@ -0,0 +1 @@ + Components - Tasmota
    Skip to content

    Components

    Component is anything wired to the ESP8266/ESP8285 chip to be controlled by or send data to it.

    Components can be: buttons, switches, relays, LEDs, sensors, displays, MCU units, etc. Every component is assigned in the device template to the GPIO it is wired (connected) to.

    Every Tasmota device has some components configured by default. Most often there is a relay, a button and a LED configured as is the case for a Sonoff Basic in the following image.

    Bug

    Tasmota 9.1 completely redesigned GPIO mapping to allow for future expansion. Read more about the GPIO Conversion

    Assigning Components~

    If you wish to expand a device with a peripheral component, after properly wiring everything, you need to assign it to a free GPIO in Configure Module page or use command GPIO<x>.

    Read more about peripherals.

    Tip

    GPIOs configured as User (1) are the GPIOs that can be assigned to components in the Configure Module page.

    GPIO Overview~

    Tasmota~

    # UI Label Comment
    0 None Not used
    32 Button1 Button active low, internal pull-up resistor
    33 Button2 Button active low, internal pull-up resistor
    34 Button3 Button active low, internal pull-up resistor
    35 Button4 Button active low, internal pull-up resistor
    36 Button5 Button active low, internal pull-up resistor
    37 Button6 Button active low, internal pull-up resistor
    38 Button7 Button active low, internal pull-up resistor
    39 Button8 Button active low, internal pull-up resistor
    64 Button_n1 Button active low, no internal pull-up resistor
    65 Button_n2 Button active low, no internal pull-up resistor
    66 Button_n3 Button active low, no internal pull-up resistor
    67 Button_n4 Button active low, no internal pull-up resistor
    68 Button_n5 Button active low, no internal pull-up resistor
    69 Button_n6 Button active low, no internal pull-up resistor
    70 Button_n7 Button active low, no internal pull-up resistor
    71 Button_n8 Button active low, no internal pull-up resistor
    96 Button_i1 Button inverted, active high with internal pull-up resistor
    97 Button_i2 Button inverted, active high with internal pull-up resistor
    98 Button_i3 Button inverted, active high with internal pull-up resistor
    99 Button_i4 Button inverted, active high with internal pull-up resistor
    100 Button_i5 Button inverted, active high with internal pull-up resistor
    101 Button_i6 Button inverted, active high with internal pull-up resistor
    102 Button_i7 Button inverted, active high with internal pull-up resistor
    103 Button_i8 Button inverted, active high with internal pull-up resistor
    128 Button_in1 Button inverted, active high no internal pull-up resistor
    129 Button_in2 Button inverted, active high no internal pull-up resistor
    130 Button_in3 Button inverted, active high no internal pull-up resistor
    131 Button_in4 Button inverted, active high no internal pull-up resistor
    132 Button_in5 Button inverted, active high no internal pull-up resistor
    133 Button_in6 Button inverted, active high no internal pull-up resistor
    134 Button_in7 Button inverted, active high no internal pull-up resistor
    135 Button_in8 Button inverted, active high no internal pull-up resistor
    160 Switch1 Switch, internal pull-up resistor
    161 Switch2 Switch, internal pull-up resistor
    162 Switch3 Switch, internal pull-up resistor
    163 Switch4 Switch, internal pull-up resistor
    164 Switch5 Switch, internal pull-up resistor
    165 Switch6 Switch, internal pull-up resistor
    166 Switch7 Switch, internal pull-up resistor
    167 Switch8 Switch, internal pull-up resistor
    192 Switch_n1 Switch, no pull-up resistor
    193 Switch_n2 Switch, no pull-up resistor
    194 Switch_n3 Switch, no pull-up resistor
    195 Switch_n4 Switch, no pull-up resistor
    196 Switch_n5 Switch, no pull-up resistor
    197 Switch_n6 Switch, no pull-up resistor
    198 Switch_n7 Switch, no pull-up resistor
    199 Switch_n8 Switch, no pull-up resistor
    224 Relay1 Relay
    225 Relay2 Relay
    226 Relay3 Relay
    227 Relay4 Relay
    228 Relay5 Relay
    229 Relay6 Relay
    230 Relay7 Relay
    231 Relay8 Relay
    255 User User pin
    256 Relay_i1 Relay inverted
    257 Relay_i2 Relay inverted
    258 Relay_i3 Relay inverted
    259 Relay_i4 Relay inverted
    260 Relay_i5 Relay inverted
    261 Relay_i6 Relay inverted
    262 Relay_i7 Relay inverted
    263 Relay_i8 Relay inverted
    288 Led1 4 x Leds
    289 Led2 4 x Leds
    290 Led3 4 x Leds
    291 Led4 4 x Leds
    320 Led_i1 4 x Leds
    321 Led_i2 4 x Leds
    322 Led_i3 4 x Leds
    323 Led_i4 4 x Leds
    352 Counter1 4 x Counter
    353 Counter2 4 x Counter
    354 Counter3 4 x Counter
    355 Counter4 4 x Counter
    384 Counter_n1 4 x Counter
    385 Counter_n2 4 x Counter
    386 Counter_n3 4 x Counter
    387 Counter_n4 4 x Counter
    416 PWM1 5 x PWM
    417 PWM2 5 x PWM
    418 PWM3 5 x PWM
    419 PWM4 5 x PWM
    420 PWM5 5 x PWM
    448 PWM_i1 5 x PWM
    449 PWM_i2 5 x PWM
    450 PWM_i3 5 x PWM
    451 PWM_i4 5 x PWM
    452 PWM_i5 5 x PWM
    480 Buzzer Buzzer
    512 Buzzer_i Buzzer
    544 LedLink Link led
    576 LedLink_i Link led
    608 I2C SCL1 Software I2C
    640 I2C SDA1 Software I2C
    672 SPI MISO1 Hardware SPI
    704 SPI MOSI1 Hardware SPI
    736 SPI CLK1 Hardware SPI
    768 SPI CS1 Hardware SPI
    800 SPI DC1 Hardware SPI
    832 SSPI MISO Software SPI
    864 SSPI MOSI Software SPI
    896 SSPI SCLK Software SPI
    928 SSPI CS Software SPI
    960 SSPI DC Software SPI
    992 Backlight Display backlight control
    1024 Display Rst OLED Display Reset
    1056 IRsend IR interface
    1088 IRrecv IR interface
    1120 RFSend RF interface
    1152 RFrecv RF interface
    1184 DHT11 DHT11
    1216 AM2301 DHT21 DHT22 AM2301 AM2302 AM2321
    1248 SI7021 SI7021
    1280 DHT11_o DHT11
    1312 DS18x20 DS18B20 or DS18S20
    1344 DS18x20_o DS18B20 or DS18S20
    1376 WS2812 WS2812 Led string
    1408 MHZ Tx MH-Z19 Serial interface
    1440 MHZ Rx MH-Z19 Serial interface
    1472 PZEM0XX Tx PZEM Serial Modbus interface
    1504 PZEM004 Rx PZEM Serial Modbus interface
    1536 PZEM016 Rx PZEM Serial Modbus interface
    1568 PZEM017 Rx PZEM Serial Modbus interface
    1600 SAir Tx SenseAir Serial interface
    1632 SAir Rx SenseAir Serial interface
    1664 PMS5003 Tx Plantower PMS5003 Serial interface
    1696 PMS5003 Rx Plantower PMS5003 Serial interface
    1728 SDS0X1 Tx Nova Fitness SDS011 Serial interface
    1760 SDS0X1 Rx Nova Fitness SDS011 Serial interface
    1792 SerBr Tx Serial Bridge Serial interface
    1824 SerBr Rx Serial Bridge Serial interface
    1856 SR04 Tri/TX SR04 interface
    1888 SR04 Ech/RX SR04 interface
    1920 SDMx20 Tx SDM120 Serial interface
    1952 SDMx20 Rx SDM120 Serial interface
    1984 SDM630 Tx SDM630 Serial interface
    2016 SDM630 Rx SDM630 Serial interface
    2048 TM1638 CLK TM1638 interface
    2080 TM1638 DIO TM1638 interface
    2112 TM1638 STB TM1638 interface
    2144 MP3 Player RB-DFR-562 DFPlayer Mini MP3 Player
    2176 HX711 SCK HX711 Load Cell interface
    2208 HX711 DAT HX711 Load Cell interface
    2240 TX2x TX20/TX23 Transmission Pin
    2272 Tuya Tx Tuya Serial interface
    2304 Tuya Rx Tuya Serial interface
    2336 MGC3130 Xfr MGC3130 interface
    2368 MGC3130 Rst MGC3130 interface
    2400 RF Sensor Rf receiver with sensor decoding
    2432 AZ Tx AZ-Instrument 7798 Serial interface
    2464 AZ Rx AZ-Instrument 7798 Serial interface
    2496 MX31855 CS MAX31855 Serial interface
    2528 MX31855 CLK MAX31855 Serial interface
    2560 MX31855 DO MAX31855 Serial interface
    2592 HLWBL SEL HLW8012/HJL-01/BL0937 energy monitoring
    2624 HLWBL SEL_i HLW8012/HJL-01/BL0937 energy monitoring
    2656 HLWBL CF1 HLW8012/HJL-01/BL0937 energy monitoring
    2688 HLW8012 CF HLW8012/HJL-01/BL0937 energy monitoring
    2720 BL0937 CF HLW8012/HJL-01/BL0937 energy monitoring
    2752 MCP39F5 Tx MCP39F501 Energy monitoring (Shelly2)
    2784 MCP39F5 Rx MCP39F501 Energy monitoring (Shelly2)
    2816 MCP39F5 Rst MCP39F501 Energy monitoring (Shelly2)
    2848 PN532 Tx PN532 NFC Serial interface
    2880 PN532 Rx PN532 NFC Serial interface
    2912 SM16716 CLK SM16716 SELECT
    2944 SM16716 DAT SM16716 SELECT
    2976 SM16716 PWR SM16716 SELECT
    3008 MY92x1 DI my92x1 PWM controller
    3040 MY92x1 DCKI my92x1 PWM controller
    3072 CSE7766 Tx CSE7766 Serial interface (S31 and Pow R2)
    3104 CSE7766 Rx CSE7766 Serial interface (S31 and Pow R2)
    3136 ALux IrRcv Arilux RF Receive input
    3168 ALux IrSel Arilux RF Receive input
    3200 Serial Tx Serial interface
    3232 Serial Rx Serial interface
    3264 RotaryA1 Rotary switch
    3265 RotaryA2 Rotary switch
    3296 RotaryB1 Rotary switch
    3297 RotaryB2 Rotary switch
    3328 ADC Joystick Analog joystick
    3360 MX31865 CS1 MAX31865 Chip Select
    3361 MX31865 CS2 MAX31865 Chip Select
    3362 MX31865 CS3 MAX31865 Chip Select
    3363 MX31865 CS4 MAX31865 Chip Select
    3364 MX31865 CS5 MAX31865 Chip Select
    3365 MX31865 CS6 MAX31865 Chip Select
    3392 HRE Clock HR-E Water Meter
    3424 HRE Data HR-E Water Meter
    3456 ADE7953 IRQ1 ADE7953 IRQ
    3457 ADE7953 IRQ2 ADE7953 IRQ
    3488 SolaxX1 Tx Solax Inverter Serial interface
    3520 SolaxX1 Rx Solax Inverter Serial interface
    3552 Zigbee Tx Zigbee Serial interface
    3584 Zigbee Rx Zigbee Serial interface
    3616 RDM6300 RX RDM6300 RX
    3648 iBeacon TX HM17 IBEACON Serial interface
    3680 iBeacon RX HM17 IBEACON Serial interface
    3712 A4988 DIR A4988 interface
    3744 A4988 STP A4988 interface
    3776 A4988 ENA A4988 interface
    3808 A4988 MS11 A4988 interface
    3809 A4988 MS12 A4988 interface
    3810 A4988 MS13 A4988 interface
    3840 Output Hi Fixed output state
    3872 Output Lo Fixed output state
    3904 DDS238-2 Tx DDS2382 Serial interface
    3936 DDS238-2 Rx DDS2382 Serial interface
    3968 DDSU666 Tx DDSU666 Serial interface
    4000 DDSU666 Rx DDSU666 Serial interface
    4032 SM2135 Clk SM2135 PWM controller
    4064 SM2135 Dat1 SM2135 PWM controller
    4065 SM2135 Dat2 SM2135 PWM controller
    4066 SM2135 Dat3 SM2135 PWM controller
    4067 SM2135 Dat4 SM2135 PWM controller
    4068 SM2135 Dat5 SM2135 PWM controller
    4069 SM2135 Dat6 SM2135 PWM controller
    4070 SM2135 Dat7 SM2135 PWM controller
    4096 DeepSleep Kill switch for deepsleep
    4128 EXS Enable EXS MCU Enable
    4160 Client TX Client Serial interface
    4192 Client RX Client Serial interface
    4224 Client RST Client Reset
    4256 Client RST_i Client Reset
    4288 HPMA Rx Honeywell HPMA115S0 Serial interface
    4320 HPMA Tx Honeywell HPMA115S0 Serial interface
    4352 GPS RX GPS Serial interface
    4384 GPS TX GPS Serial interface
    4416 HM10 RX HM10-BLE-Mijia-bridge Serial interface
    4448 HM10 TX HM10-BLE-Mijia-bridge Serial interface
    4480 LE-01MR Rx F&F LE-01MR energy meter
    4512 LE-01MR Tx F&F LE-01MR energy meter
    4544 CC1101 GDO0 CC1101 Serial interface
    4576 CC1101 GDO2 CC1101 Serial interface
    4608 HRXL Rx Data from MaxBotix HRXL sonar range sensor
    4640 MOODL Tx ElectriQ iQ-wifiMOODL Serial TX
    4672 AS3935 Franklin Lightning Sensor
    4704 ADC Input Analog input
    4736 ADC Temp Analog Thermistor
    4768 ADC Light Analog Light sensor
    4800 ADC Button Analog Button
    4832 ADC Button_i Analog Button
    4864 ADC Range Analog Range
    4896 ADC CT Power ANalog Current
    4928 OpenTherm RX OpenTherm Boiler TX pin
    4960 OpenTherm TX OpenTherm Boiler TX pin
    4992 WindMeter Spd WindMeter speed counter pin
    5024 Button_tc Touch pin as button
    5056 BL0940 Rx BL0940 serial interface
    5088 TCP Tx TCP to serial bridge
    5120 TCP Rx TCP to serial bridge
    5152 TInfo Rx Teleinfo telemetry data receive pin
    5184 TInfo EN Teleinfo Enable Receive Pin
    5216 LMT01 Pulse LMT01 input counting pin
    5248 iEM3000 TX IEM3000 Serial interface
    5280 iEM3000 RX IEM3000 Serial interface
    5312 Zigbee Rst1 Zigbee reset
    5313 Zigbee Rst2 Zigbee reset
    5344 DYP Rx
    5376 MiEl HVAC Tx Mitsubishi Electric HVAC
    5408 MiEl HVAC Rx Mitsubishi Electric HVAC
    5440 WE517 Tx ORNO WE517 Serial interface
    5472 WE517 Rx ORNO WE517 Serial interface
    5504 AS608 Tx Serial interface AS608 / R503
    5536 AS608 Rx Serial interface AS608 / R503
    5568 SHD Boot 0
    5600 SHD Reset
    5632 RC522 Rst RC522 reset
    5664 P9813 Clk P9813 Clock and Data
    5696 P9813 Dat P9813 Clock and Data
    5728 OptionA1 Specific device options to be served in code
    5729 OptionA2 Specific device options to be served in code
    5730 OptionA3 Specific device options to be served in code
    5731 OptionA4 Specific device options to be served in code
    5732 OptionA5 Specific device options to be served in code
    5733 OptionA6 Specific device options to be served in code
    5760 FTC532 FTC532 touch ctrlr serial input
    5792 RC522 CS
    5824 NRF24 CS
    5856 NRF24 DC
    5888 ILI9341 CS
    5920 ILI9341 DC
    5952 ILI9488 CS
    5984 EPaper29 CS
    6016 EPaper42 CS
    6048 SSD1351 CS
    6080 RA8876 CS
    6112 ST7789 CS
    6144 ST7789 DC
    6176 SSD1331 CS
    6208 SSD1331 DC
    6240 SDCard CS
    6272 RotaryA_n1 Rotary switch
    6273 RotaryA_n2 Rotary switch
    6304 RotaryB_n1 Rotary switch
    6305 RotaryB_n2 Rotary switch
    6336 ADC pH Analog PH Sensor
    6368 BS814 CLK Holtek BS814A2 touch ctrlr
    6400 BS814 DAT Holtek BS814A2 touch ctrlr
    6432 Wiegand D0 Wiegand Data lines
    6464 Wiegand D1 Wiegand Data lines
    6496 NeoPool Tx Sugar Valley RS485 interface
    6528 NeoPool Rx Sugar Valley RS485 interface
    6560 SDM72 Tx SDM72 Serial interface
    6592 SDM72 Rx SDM72 Serial interface
    6624 TM1637 CLK TM1637 interface
    6656 TM1637 DIO TM1637 interface
    6688 DLP Tx LCD/DLP Projector Serial Control
    6720 DLP Rx LCD/DLP Projector Serial Control
    6752 SSD1351 DC
    6784 XPT2046 CS XPT2046 SPI Chip Select
    6816 CSE7761 Tx CSE7761 Serial interface (Dual R3)
    6848 CSE7761 Rx CSE7761 Serial interface (Dual R3)
    6880 VL53LXX XSHUT1 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    6881 VL53LXX XSHUT2 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    6882 VL53LXX XSHUT3 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    6883 VL53LXX XSHUT4 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    6884 VL53LXX XSHUT5 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    6885 VL53LXX XSHUT6 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    6886 VL53LXX XSHUT7 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    6887 VL53LXX XSHUT8 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    6912 MAX7219 CLK MAX7219 interface
    6944 MAX7219 DIN MAX7219 interface
    6976 MAX7219 CS MAX7219 interface
    7008 TFmini+ TX TFmini Plus ToF sensor
    7040 TFmini+ RX TFmini Plus ToF sensor
    7072 ZC Pulse
    7104 Input1
    7105 Input2
    7106 Input3
    7107 Input4
    7108 Input5
    7109 Input6
    7110 Input7
    7111 Input8
    7136 I2S Out Data1
    7168 I2S Out Clk1
    7200 I2S Out Slct1
    7232 I2S In Data1
    7264 I2S In Clk1
    7296 I2S In Slct1
    7328 Interrupt1
    7329 Interrupt2
    7330 Interrupt3
    7331 Interrupt4
    7332 Interrupt5
    7333 Interrupt6
    7334 Interrupt7
    7335 Interrupt8
    7360 MCP2515 CS MCP2515 Chip Select
    7392 HRG15 Tx Hydreon RG-15 rain sensor serial interface
    7424 HRG15 Rx Hydreon RG-15 rain sensor serial interface
    7456 VINDRIKTNING IKEA VINDRIKTNING Serial interface
    7488 BL0939 Rx BL0939 Serial interface (Dual R3 v2)
    7520 BL0942 Rx BL0942 Serial interface
    7552 HM330X SET HM330X SET pin (sleep when low)
    7584 Heartbeat
    7616 Heartbeat_i
    7648 74x595 SRCLK 74x595 Shift register
    7680 74x595 RCLK 74x595 Shift register
    7712 74x595 OE 74x595 Shift register
    7744 74x595 SER 74x595 Shift register
    7776 SolaxX1 RTS Solax Inverter Serial interface
    7808 OptionE Emulated module
    7840 SDM230 Tx SDM230 Serial interface
    7872 SDM230 Rx SDM230 Serial interface
    7904 ADC MQ Analog MQ Sensor
    7936 CM110x TX CM11 Serial interface
    7968 CM110x RX CM11 Serial interface
    8000 BL6523 Tx BL6523 based Watt meter Serial interface
    8032 BL6523 Rx BL6523 based Watt meter Serial interface
    8064 ADE7880 IRQ1 ADE7880 IRQ
    8065 ADE7880 IRQ2 ADE7880 IRQ
    8096 Reset Generic reset
    8128 MS01 Sonoff MS01 Moisture Sensor 1wire interface
    8160 SDIO CMD SD Card SDIO interface including 1-bit and 4-bit modes
    8192 SDIO CLK SD Card SDIO interface including 1-bit and 4-bit modes
    8224 SDIO D0 SD Card SDIO interface including 1-bit and 4-bit modes
    8256 SDIO D1 SD Card SDIO interface including 1-bit and 4-bit modes
    8288 SDIO D2 SD Card SDIO interface including 1-bit and 4-bit modes
    8320 SDIO D3 SD Card SDIO interface including 1-bit and 4-bit modes

    Tasmota32~

    # UI Label Comment
    0 None Not used
    1 User User pin
    32 Button1 4 x Button
    33 Button2 4 x Button
    34 Button3 4 x Button
    35 Button4 4 x Button
    36 Button5 4 x Button
    37 Button6 4 x Button
    38 Button7 4 x Button
    39 Button8 4 x Button
    64 Button_n1 4 x Button
    65 Button_n2 4 x Button
    66 Button_n3 4 x Button
    67 Button_n4 4 x Button
    68 Button_n5 4 x Button
    69 Button_n6 4 x Button
    70 Button_n7 4 x Button
    71 Button_n8 4 x Button
    96 Button_i1 4 x Button
    97 Button_i2 4 x Button
    98 Button_i3 4 x Button
    99 Button_i4 4 x Button
    100 Button_i5 4 x Button
    101 Button_i6 4 x Button
    102 Button_i7 4 x Button
    103 Button_i8 4 x Button
    128 Button_in1 4 x Button
    129 Button_in2 4 x Button
    130 Button_in3 4 x Button
    131 Button_in4 4 x Button
    132 Button_in5 4 x Button
    133 Button_in6 4 x Button
    134 Button_in7 4 x Button
    135 Button_in8 4 x Button
    160 Switch1 8 x User connected external switches
    161 Switch2 8 x User connected external switches
    162 Switch3 8 x User connected external switches
    163 Switch4 8 x User connected external switches
    164 Switch5 8 x User connected external switches
    165 Switch6 8 x User connected external switches
    166 Switch7 8 x User connected external switches
    167 Switch8 8 x User connected external switches
    168 Switch9 8 x User connected external switches
    169 Switch10 8 x User connected external switches
    170 Switch11 8 x User connected external switches
    171 Switch12 8 x User connected external switches
    172 Switch13 8 x User connected external switches
    173 Switch14 8 x User connected external switches
    174 Switch15 8 x User connected external switches
    175 Switch16 8 x User connected external switches
    176 Switch17 8 x User connected external switches
    177 Switch18 8 x User connected external switches
    178 Switch19 8 x User connected external switches
    179 Switch20 8 x User connected external switches
    180 Switch21 8 x User connected external switches
    181 Switch22 8 x User connected external switches
    182 Switch23 8 x User connected external switches
    183 Switch24 8 x User connected external switches
    184 Switch25 8 x User connected external switches
    185 Switch26 8 x User connected external switches
    186 Switch27 8 x User connected external switches
    187 Switch28 8 x User connected external switches
    192 Switch_n1 8 x User connected external switches
    193 Switch_n2 8 x User connected external switches
    194 Switch_n3 8 x User connected external switches
    195 Switch_n4 8 x User connected external switches
    196 Switch_n5 8 x User connected external switches
    197 Switch_n6 8 x User connected external switches
    198 Switch_n7 8 x User connected external switches
    199 Switch_n8 8 x User connected external switches
    200 Switch_n9 8 x User connected external switches
    201 Switch_n10 8 x User connected external switches
    202 Switch_n11 8 x User connected external switches
    203 Switch_n12 8 x User connected external switches
    204 Switch_n13 8 x User connected external switches
    205 Switch_n14 8 x User connected external switches
    206 Switch_n15 8 x User connected external switches
    207 Switch_n16 8 x User connected external switches
    208 Switch_n17 8 x User connected external switches
    209 Switch_n18 8 x User connected external switches
    210 Switch_n19 8 x User connected external switches
    211 Switch_n20 8 x User connected external switches
    212 Switch_n21 8 x User connected external switches
    213 Switch_n22 8 x User connected external switches
    214 Switch_n23 8 x User connected external switches
    215 Switch_n24 8 x User connected external switches
    216 Switch_n25 8 x User connected external switches
    217 Switch_n26 8 x User connected external switches
    218 Switch_n27 8 x User connected external switches
    219 Switch_n28 8 x User connected external switches
    224 Relay1 8 x Relays
    225 Relay2 8 x Relays
    226 Relay3 8 x Relays
    227 Relay4 8 x Relays
    228 Relay5 8 x Relays
    229 Relay6 8 x Relays
    230 Relay7 8 x Relays
    231 Relay8 8 x Relays
    232 Relay9 8 x Relays
    233 Relay10 8 x Relays
    234 Relay11 8 x Relays
    235 Relay12 8 x Relays
    236 Relay13 8 x Relays
    237 Relay14 8 x Relays
    238 Relay15 8 x Relays
    239 Relay16 8 x Relays
    240 Relay17 8 x Relays
    241 Relay18 8 x Relays
    242 Relay19 8 x Relays
    243 Relay20 8 x Relays
    244 Relay21 8 x Relays
    245 Relay22 8 x Relays
    246 Relay23 8 x Relays
    247 Relay24 8 x Relays
    248 Relay25 8 x Relays
    249 Relay26 8 x Relays
    250 Relay27 8 x Relays
    251 Relay28 8 x Relays
    256 Relay_i1 8 x Relays
    257 Relay_i2 8 x Relays
    258 Relay_i3 8 x Relays
    259 Relay_i4 8 x Relays
    260 Relay_i5 8 x Relays
    261 Relay_i6 8 x Relays
    262 Relay_i7 8 x Relays
    263 Relay_i8 8 x Relays
    264 Relay_i9 8 x Relays
    265 Relay_i10 8 x Relays
    266 Relay_i11 8 x Relays
    267 Relay_i12 8 x Relays
    268 Relay_i13 8 x Relays
    269 Relay_i14 8 x Relays
    270 Relay_i15 8 x Relays
    271 Relay_i16 8 x Relays
    272 Relay_i17 8 x Relays
    273 Relay_i18 8 x Relays
    274 Relay_i19 8 x Relays
    275 Relay_i20 8 x Relays
    276 Relay_i21 8 x Relays
    277 Relay_i22 8 x Relays
    278 Relay_i23 8 x Relays
    279 Relay_i24 8 x Relays
    280 Relay_i25 8 x Relays
    281 Relay_i26 8 x Relays
    282 Relay_i27 8 x Relays
    283 Relay_i28 8 x Relays
    288 Led1 4 x Leds
    289 Led2 4 x Leds
    290 Led3 4 x Leds
    291 Led4 4 x Leds
    320 Led_i1 4 x Leds
    321 Led_i2 4 x Leds
    322 Led_i3 4 x Leds
    323 Led_i4 4 x Leds
    352 Counter1 4 x Counter
    353 Counter2 4 x Counter
    354 Counter3 4 x Counter
    355 Counter4 4 x Counter
    384 Counter_n1 4 x Counter
    385 Counter_n2 4 x Counter
    386 Counter_n3 4 x Counter
    387 Counter_n4 4 x Counter
    416 PWM1 5 x PWM
    417 PWM2 5 x PWM
    418 PWM3 5 x PWM
    419 PWM4 5 x PWM
    420 PWM5 5 x PWM
    448 PWM_i1 5 x PWM
    449 PWM_i2 5 x PWM
    450 PWM_i3 5 x PWM
    451 PWM_i4 5 x PWM
    452 PWM_i5 5 x PWM
    480 Buzzer Buzzer
    512 Buzzer_i Buzzer
    544 LedLink Link led
    576 LedLink_i Link led
    608 I2C SCL1 Software I2C
    609 I2C SCL2 Software I2C
    640 I2C SDA1 Software I2C
    641 I2C SDA2 Software I2C
    672 SPI MISO1 Hardware SPI
    673 SPI MISO2 Hardware SPI
    704 SPI MOSI1 Hardware SPI
    705 SPI MOSI2 Hardware SPI
    736 SPI CLK1 Hardware SPI
    737 SPI CLK2 Hardware SPI
    768 SPI CS1 Hardware SPI
    769 SPI CS2 Hardware SPI
    800 SPI DC1 Hardware SPI
    801 SPI DC2 Hardware SPI
    832 SSPI MISO Software SPI
    864 SSPI MOSI Software SPI
    896 SSPI SCLK Software SPI
    928 SSPI CS Software SPI
    960 SSPI DC Software SPI
    992 Backlight Display backlight control
    1024 Display Rst OLED Display Reset
    1056 IRsend IR interface
    1088 IRrecv IR interface
    1120 RFSend RF interface
    1152 RFrecv RF interface
    1184 DHT11 DHT11 DHT21 DHT22 AM2301 AM2302 AM2321
    1216 AM2301 DHT11 DHT21 DHT22 AM2301 AM2302 AM2321
    1248 SI7021 DHT11 DHT21 DHT22 AM2301 AM2302 AM2321
    1280 DHT11_o DHT11 DHT21 DHT22 AM2301 AM2302 AM2321
    1312 DS18x20 DS18B20 or DS18S20
    1344 DS18x20_o DS18B20 or DS18S20
    1376 WS2812 WS2812 Led string
    1408 MHZ Tx MH-Z19 Serial interface
    1440 MHZ Rx MH-Z19 Serial interface
    1472 PZEM0XX Tx PZEM Serial Modbus interface
    1504 PZEM004 Rx PZEM Serial Modbus interface
    1536 PZEM016 Rx PZEM Serial Modbus interface
    1568 PZEM017 Rx PZEM Serial Modbus interface
    1600 SAir Tx SenseAir Serial interface
    1632 SAir Rx SenseAir Serial interface
    1664 PMS5003 Tx Plantower PMS5003 Serial interface
    1696 PMS5003 Rx Plantower PMS5003 Serial interface
    1728 SDS0X1 Tx Nova Fitness SDS011 Serial interface
    1760 SDS0X1 Rx Nova Fitness SDS011 Serial interface
    1792 SerBr Tx Serial Bridge Serial interface
    1824 SerBr Rx Serial Bridge Serial interface
    1856 SR04 Tri/TX SR04 interface
    1888 SR04 Ech/RX SR04 interface
    1920 SDMx20 Tx SDM120 Serial interface
    1952 SDMx20 Rx SDM120 Serial interface
    1984 SDM630 Tx SDM630 Serial interface
    2016 SDM630 Rx SDM630 Serial interface
    2048 TM1638 CLK TM1638 interface
    2080 TM1638 DIO TM1638 interface
    2112 TM1638 STB TM1638 interface
    2144 MP3 Player RB-DFR-562 DFPlayer Mini MP3 Player
    2176 HX711 SCK HX711 Load Cell interface
    2208 HX711 DAT HX711 Load Cell interface
    2240 TX2x TX20/TX23 Transmission Pin
    2272 Tuya Tx Tuya Serial interface
    2304 Tuya Rx Tuya Serial interface
    2336 MGC3130 Xfr MGC3130 interface
    2368 MGC3130 Rst MGC3130 interface
    2400 RF Sensor Rf receiver with sensor decoding
    2432 AZ Tx AZ-Instrument 7798 Serial interface
    2464 AZ Rx AZ-Instrument 7798 Serial interface
    2496 MX31855 CS MAX31855 Serial interface
    2528 MX31855 CLK MAX31855 Serial interface
    2560 MX31855 DO MAX31855 Serial interface
    2592 HLWBL SEL HLW8012/HJL-01/BL0937 energy monitoring
    2624 HLWBL SEL_i HLW8012/HJL-01/BL0937 energy monitoring
    2656 HLWBL CF1 HLW8012/HJL-01/BL0937 energy monitoring
    2688 HLW8012 CF HLW8012/HJL-01/BL0937 energy monitoring
    2720 BL0937 CF HLW8012/HJL-01/BL0937 energy monitoring
    2752 MCP39F5 Tx MCP39F501 Energy monitoring (Shelly2)
    2784 MCP39F5 Rx MCP39F501 Energy monitoring (Shelly2)
    2816 MCP39F5 Rst MCP39F501 Energy monitoring (Shelly2)
    2848 PN532 Tx PN532 NFC Serial interface
    2880 PN532 Rx PN532 NFC Serial interface
    2912 SM16716 CLK SM16716 SELECT
    2944 SM16716 DAT SM16716 SELECT
    2976 SM16716 PWR SM16716 SELECT
    3008 MY92x1 DI my92x1 PWM controller
    3040 MY92x1 DCKI my92x1 PWM controller
    3072 CSE7766 Tx CSE7766 Serial interface (S31 and Pow R2)
    3104 CSE7766 Rx CSE7766 Serial interface (S31 and Pow R2)
    3136 ALux IrRcv Arilux RF Receive input
    3168 ALux IrSel Arilux RF Receive input
    3200 Serial Tx Serial interface
    3232 Serial Rx Serial interface
    3264 RotaryA1 Rotary switch
    3265 RotaryA2 Rotary switch
    3296 RotaryB1 Rotary switch
    3297 RotaryB2 Rotary switch
    3328 ADC Joystick1 Analog joystick
    3329 ADC Joystick2 Analog joystick
    3330 ADC Joystick3 Analog joystick
    3331 ADC Joystick4 Analog joystick
    3332 ADC Joystick5 Analog joystick
    3333 ADC Joystick6 Analog joystick
    3334 ADC Joystick7 Analog joystick
    3335 ADC Joystick8 Analog joystick
    3360 MX31865 CS1 MAX31865 Chip Select
    3361 MX31865 CS2 MAX31865 Chip Select
    3362 MX31865 CS3 MAX31865 Chip Select
    3363 MX31865 CS4 MAX31865 Chip Select
    3364 MX31865 CS5 MAX31865 Chip Select
    3365 MX31865 CS6 MAX31865 Chip Select
    3392 HRE Clock HR-E Water Meter
    3424 HRE Data HR-E Water Meter
    3456 ADE7953 IRQ1 ADE7953 IRQ
    3457 ADE7953 IRQ2 ADE7953 IRQ
    3488 SolaxX1 Tx Solax Inverter Serial interface
    3520 SolaxX1 Rx Solax Inverter Serial interface
    3552 Zigbee Tx Zigbee Serial interface
    3584 Zigbee Rx Zigbee Serial interface
    3616 RDM6300 RX RDM6300 RX
    3648 iBeacon TX HM17 IBEACON Serial interface
    3680 iBeacon RX HM17 IBEACON Serial interface
    3712 A4988 DIR A4988 interface
    3744 A4988 STP A4988 interface
    3776 A4988 ENA A4988 interface
    3808 A4988 MS11 A4988 interface
    3809 A4988 MS12 A4988 interface
    3810 A4988 MS13 A4988 interface
    3840 Output Hi Fixed output state
    3872 Output Lo Fixed output state
    3904 DDS238-2 Tx DDS2382 Serial interface
    3936 DDS238-2 Rx DDS2382 Serial interface
    3968 DDSU666 Tx DDSU666 Serial interface
    4000 DDSU666 Rx DDSU666 Serial interface
    4032 SM2135 Clk SM2135 PWM controller
    4064 SM2135 Dat1 SM2135 PWM controller
    4065 SM2135 Dat2 SM2135 PWM controller
    4066 SM2135 Dat3 SM2135 PWM controller
    4067 SM2135 Dat4 SM2135 PWM controller
    4068 SM2135 Dat5 SM2135 PWM controller
    4069 SM2135 Dat6 SM2135 PWM controller
    4070 SM2135 Dat7 SM2135 PWM controller
    4096 DeepSleep Kill switch for deepsleep
    4128 EXS Enable EXS MCU Enable
    4160 Client TX Client Serial interface
    4192 Client RX Client Serial interface
    4224 Client RST Client Reset
    4256 Client RST_i Client Reset
    4288 HPMA Rx Honeywell HPMA115S0 Serial interface
    4320 HPMA Tx Honeywell HPMA115S0 Serial interface
    4352 GPS RX GPS Serial interface
    4384 GPS TX GPS Serial interface
    4416 HM10 RX HM10-BLE-Mijia-bridge Serial interface
    4448 HM10 TX HM10-BLE-Mijia-bridge Serial interface
    4480 LE-01MR Rx F&F LE-01MR energy meter
    4512 LE-01MR Tx F&F LE-01MR energy meter
    4544 CC1101 GDO0 CC1101 Serial interface
    4576 CC1101 GDO2 CC1101 Serial interface
    4608 HRXL Rx Data from MaxBotix HRXL sonar range sensor
    4640 MOODL Tx ElectriQ iQ-wifiMOODL Serial TX
    4672 AS3935 Franklin Lightning Sensor
    4704 ADC Input1 Analog input
    4705 ADC Input2 Analog input
    4706 ADC Input3 Analog input
    4707 ADC Input4 Analog input
    4708 ADC Input5 Analog input
    4709 ADC Input6 Analog input
    4710 ADC Input7 Analog input
    4711 ADC Input8 Analog input
    4736 ADC Temp1 Analog Thermistor
    4737 ADC Temp2 Analog Thermistor
    4738 ADC Temp3 Analog Thermistor
    4739 ADC Temp4 Analog Thermistor
    4740 ADC Temp5 Analog Thermistor
    4741 ADC Temp6 Analog Thermistor
    4742 ADC Temp7 Analog Thermistor
    4743 ADC Temp8 Analog Thermistor
    4768 ADC Light1 Analog Light sensor
    4769 ADC Light2 Analog Light sensor
    4770 ADC Light3 Analog Light sensor
    4771 ADC Light4 Analog Light sensor
    4772 ADC Light5 Analog Light sensor
    4773 ADC Light6 Analog Light sensor
    4774 ADC Light7 Analog Light sensor
    4775 ADC Light8 Analog Light sensor
    4800 ADC Button1 Analog Button
    4801 ADC Button2 Analog Button
    4802 ADC Button3 Analog Button
    4803 ADC Button4 Analog Button
    4804 ADC Button5 Analog Button
    4805 ADC Button6 Analog Button
    4806 ADC Button7 Analog Button
    4807 ADC Button8 Analog Button
    4832 ADC Button_i1 Analog Button
    4833 ADC Button_i2 Analog Button
    4834 ADC Button_i3 Analog Button
    4835 ADC Button_i4 Analog Button
    4836 ADC Button_i5 Analog Button
    4837 ADC Button_i6 Analog Button
    4838 ADC Button_i7 Analog Button
    4839 ADC Button_i8 Analog Button
    4864 ADC Range1 Analog Range
    4865 ADC Range2 Analog Range
    4866 ADC Range3 Analog Range
    4867 ADC Range4 Analog Range
    4868 ADC Range5 Analog Range
    4869 ADC Range6 Analog Range
    4870 ADC Range7 Analog Range
    4871 ADC Range8 Analog Range
    4896 ADC CT Power1 ANalog Current
    4897 ADC CT Power2 ANalog Current
    4898 ADC CT Power3 ANalog Current
    4899 ADC CT Power4 ANalog Current
    4900 ADC CT Power5 ANalog Current
    4901 ADC CT Power6 ANalog Current
    4902 ADC CT Power7 ANalog Current
    4903 ADC CT Power8 ANalog Current
    4928 CAM_PWDN Webcam
    4960 CAM_RESET Webcam
    4992 CAM_XCLK Webcam
    5024 CAM_SIOD Webcam I2C
    5056 CAM_SIOC Webcam I2C
    5088 CAM_DATA1
    5089 CAM_DATA2
    5090 CAM_DATA3
    5091 CAM_DATA4
    5092 CAM_DATA5
    5093 CAM_DATA6
    5094 CAM_DATA7
    5095 CAM_DATA8
    5120 CAM_VSYNC
    5152 CAM_HREF
    5184 CAM_PCLK
    5216 CAM_PSCLK
    5248 CAM_HSD1
    5249 CAM_HSD2
    5250 CAM_HSD3
    5280 CAM_PSRCS
    5312 OpenTherm RX OpenTherm Boiler TX pin
    5344 OpenTherm TX OpenTherm Boiler TX pin
    5376 WindMeter Spd WindMeter speed counter pin
    5408 Button_tc1 Touch pin as button
    5409 Button_tc2 Touch pin as button
    5410 Button_tc3 Touch pin as button
    5411 Button_tc4 Touch pin as button
    5412 Button_tc5 Touch pin as button
    5413 Button_tc6 Touch pin as button
    5414 Button_tc7 Touch pin as button
    5415 Button_tc8 Touch pin as button
    5440 BL0940 Rx BL0940 serial interface
    5472 TCP Tx TCP to serial bridge
    5504 TCP Rx TCP to serial bridge
    5536 ETH POWER Ethernet
    5568 ETH MDC Ethernet
    5600 ETH MDIO Ethernet
    5632 TInfo Rx Teleinfo telemetry data receive pin
    5664 TInfo EN Teleinfo Enable Receive Pin
    5696 LMT01 Pulse LMT01 input counting pin
    5728 iEM3000 TX IEM3000 Serial interface
    5760 iEM3000 RX IEM3000 Serial interface
    5792 Zigbee Rst1 Zigbee reset
    5793 Zigbee Rst2 Zigbee reset
    5824 DYP Rx
    5856 MiEl HVAC Tx Mitsubishi Electric HVAC
    5888 MiEl HVAC Rx Mitsubishi Electric HVAC
    5920 WE517 Tx ORNO WE517 Serial interface
    5952 WE517 Rx ORNO WE517 Serial interface
    5984 AS608 Tx Serial interface AS608 / R503
    6016 AS608 Rx Serial interface AS608 / R503
    6048 SHD Boot 0
    6080 SHD Reset
    6112 RC522 Rst RC522 reset
    6144 P9813 Clk P9813 Clock and Data
    6176 P9813 Dat P9813 Clock and Data
    6208 OptionA1 Specific device options to be served in code
    6209 OptionA2 Specific device options to be served in code
    6210 OptionA3 Specific device options to be served in code
    6211 OptionA4 Specific device options to be served in code
    6212 OptionA5 Specific device options to be served in code
    6213 OptionA6 Specific device options to be served in code
    6240 FTC532 FTC532 touch ctrlr serial input
    6272 RC522 CS
    6304 NRF24 CS
    6336 NRF24 DC
    6368 ILI9341 CS
    6400 ILI9341 DC
    6432 ILI9488 CS
    6464 EPaper29 CS
    6496 EPaper42 CS
    6528 SSD1351 CS
    6560 RA8876 CS
    6592 ST7789 CS
    6624 ST7789 DC
    6656 SSD1331 CS
    6688 SSD1331 DC
    6720 SDCard CS
    6752 RotaryA_n1 Rotary switch
    6753 RotaryA_n2 Rotary switch
    6784 RotaryB_n1 Rotary switch
    6785 RotaryB_n2 Rotary switch
    6816 ADC pH1 Analog PH Sensor
    6817 ADC pH2 Analog PH Sensor
    6818 ADC pH3 Analog PH Sensor
    6819 ADC pH4 Analog PH Sensor
    6820 ADC pH5 Analog PH Sensor
    6821 ADC pH6 Analog PH Sensor
    6822 ADC pH7 Analog PH Sensor
    6823 ADC pH8 Analog PH Sensor
    6848 BS814 CLK Holtek BS814A2 touch ctrlr
    6880 BS814 DAT Holtek BS814A2 touch ctrlr
    6912 Wiegand D0 Wiegand Data lines
    6944 Wiegand D1 Wiegand Data lines
    6976 NeoPool Tx Sugar Valley RS485 interface
    7008 NeoPool Rx Sugar Valley RS485 interface
    7040 SDM72 Tx SDM72 Serial interface
    7072 SDM72 Rx SDM72 Serial interface
    7104 TM1637 CLK TM1637 interface
    7136 TM1637 DIO TM1637 interface
    7168 DLP Tx LCD/DLP Projector Serial Control
    7200 DLP Rx LCD/DLP Projector Serial Control
    7232 SSD1351 DC
    7264 XPT2046 CS XPT2046 SPI Chip Select
    7296 CSE7761 Tx CSE7761 Serial interface (Dual R3)
    7328 CSE7761 Rx CSE7761 Serial interface (Dual R3)
    7360 VL53LXX XSHUT1 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    7361 VL53LXX XSHUT2 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    7362 VL53LXX XSHUT3 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    7363 VL53LXX XSHUT4 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    7364 VL53LXX XSHUT5 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    7365 VL53LXX XSHUT6 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    7366 VL53LXX XSHUT7 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    7367 VL53LXX XSHUT8 VL53LXX_XSHUT (the max number of sensors is VL53LXX_MAX_SENSORS)- Used when connecting multiple VL53LXX
    7392 MAX7219 CLK MAX7219 interface
    7424 MAX7219 DIN MAX7219 interface
    7456 MAX7219 CS MAX7219 interface
    7488 TFmini+ TX TFmini Plus ToF sensor
    7520 TFmini+ RX TFmini Plus ToF sensor
    7552 ZC Pulse
    7584 HallEffect1
    7585 HallEffect2
    7616 EPD Data Base connection EPD driver
    7648 Input1
    7649 Input2
    7650 Input3
    7651 Input4
    7652 Input5
    7653 Input6
    7654 Input7
    7655 Input8
    7656 Input9
    7657 Input10
    7658 Input11
    7659 Input12
    7660 Input13
    7661 Input14
    7662 Input15
    7663 Input16
    7664 Input17
    7665 Input18
    7666 Input19
    7667 Input20
    7668 Input21
    7669 Input22
    7670 Input23
    7671 Input24
    7672 Input25
    7673 Input26
    7674 Input27
    7675 Input28
    7680 Button_d1
    7681 Button_d2
    7682 Button_d3
    7683 Button_d4
    7684 Button_d5
    7685 Button_d6
    7686 Button_d7
    7687 Button_d8
    7712 Button_id1
    7713 Button_id2
    7714 Button_id3
    7715 Button_id4
    7716 Button_id5
    7717 Button_id6
    7718 Button_id7
    7719 Button_id8
    7744 Switch_d1
    7745 Switch_d2
    7746 Switch_d3
    7747 Switch_d4
    7748 Switch_d5
    7749 Switch_d6
    7750 Switch_d7
    7751 Switch_d8
    7752 Switch_d9
    7753 Switch_d10
    7754 Switch_d11
    7755 Switch_d12
    7756 Switch_d13
    7757 Switch_d14
    7758 Switch_d15
    7759 Switch_d16
    7760 Switch_d17
    7761 Switch_d18
    7762 Switch_d19
    7763 Switch_d20
    7764 Switch_d21
    7765 Switch_d22
    7766 Switch_d23
    7767 Switch_d24
    7768 Switch_d25
    7769 Switch_d26
    7770 Switch_d27
    7771 Switch_d28
    7776 I2S Out Data1
    7777 I2S Out Data2
    7808 I2S Out Clk1
    7809 I2S Out Clk2
    7840 I2S Out Slct1
    7841 I2S Out Slct2
    7872 I2S In Data1
    7873 I2S In Data2
    7904 I2S In Clk1
    7905 I2S In Clk2
    7936 I2S In Slct1
    7937 I2S In Slct2
    7968 Interrupt1
    7969 Interrupt2
    7970 Interrupt3
    7971 Interrupt4
    7972 Interrupt5
    7973 Interrupt6
    7974 Interrupt7
    7975 Interrupt8
    7976 Interrupt9
    7977 Interrupt10
    7978 Interrupt11
    7979 Interrupt12
    7980 Interrupt13
    7981 Interrupt14
    7982 Interrupt15
    7983 Interrupt16
    7984 Interrupt17
    7985 Interrupt18
    7986 Interrupt19
    7987 Interrupt20
    7988 Interrupt21
    7989 Interrupt22
    7990 Interrupt23
    7991 Interrupt24
    7992 Interrupt25
    7993 Interrupt26
    7994 Interrupt27
    7995 Interrupt28
    8000 MCP2515 CS MCP2515 Chip Select
    8032 HRG15 Tx Hydreon RG-15 rain sensor serial interface
    8064 HRG15 Rx Hydreon RG-15 rain sensor serial interface
    8096 VINDRIKTNING IKEA VINDRIKTNING Serial interface
    8128 BL0939 Rx BL0939 Serial interface (Dual R3 v2)
    8160 BL0942 Rx BL0942 Serial interface
    8192 HM330X SET HM330X SET pin (sleep when low)
    8224 Heartbeat
    8256 Heartbeat_i
    8288 74x595 SRCLK 74x595 Shift register
    8320 74x595 RCLK 74x595 Shift register
    8352 74x595 OE 74x595 Shift register
    8384 74x595 SER 74x595 Shift register
    8416 SolaxX1 RTS Solax Inverter Serial interface
    8448 OptionE1 Emulated module
    8480 SDM230 Tx SDM230 Serial interface
    8512 SDM230 Rx SDM230 Serial interface
    8544 ADC MQ1 Analog MQ Sensor
    8545 ADC MQ2 Analog MQ Sensor
    8546 ADC MQ3 Analog MQ Sensor
    8547 ADC MQ4 Analog MQ Sensor
    8548 ADC MQ5 Analog MQ Sensor
    8549 ADC MQ6 Analog MQ Sensor
    8550 ADC MQ7 Analog MQ Sensor
    8551 ADC MQ8 Analog MQ Sensor
    8576 CM110x TX CM11 Serial interface
    8608 CM110x RX CM11 Serial interface
    8640 BL6523 Tx BL6523 based Watt meter Serial interface
    8672 BL6523 Rx BL6523 based Watt meter Serial interface
    8704 ADE7880 IRQ1 ADE7880 IRQ
    8705 ADE7880 IRQ2 ADE7880 IRQ
    8736 Reset Generic reset
    8768 MS01 Sonoff MS01 Moisture Sensor 1wire interface
    8800 SDIO CMD SD Card SDIO interface including 1-bit and 4-bit modes
    8832 SDIO CLK SD Card SDIO interface including 1-bit and 4-bit modes
    8864 SDIO D0 SD Card SDIO interface including 1-bit and 4-bit modes
    8896 SDIO D1 SD Card SDIO interface including 1-bit and 4-bit modes
    8928 SDIO D2 SD Card SDIO interface including 1-bit and 4-bit modes
    8960 SDIO D3 SD Card SDIO interface including 1-bit and 4-bit modes
    \ No newline at end of file diff --git a/Configuration-Procedure-for-New-Devices/index.html b/Configuration-Procedure-for-New-Devices/index.html new file mode 100644 index 0000000000..e02d74e3fa --- /dev/null +++ b/Configuration-Procedure-for-New-Devices/index.html @@ -0,0 +1,10 @@ + Configure Unknown Device - Tasmota
    Skip to content

    Configuration procedure for new devices~

    If your device is not a built-in module listed in the module configuration menu, a user contributed device template (explanation) may be available. Otherwise, follow this procedure for configuring the ESP chip pins used by your device.

    Some smart devices have additional functionality which may be handled by codes sent to a separate MCU in the device. Devices with functions offloaded to a separate MCU require additional coding in the software or via rules. Certain supported Tasmota BASE devices have built-in code to handle MCU controlled devices. Using a template with an appropriate BASE device may have the programming logic required to manage the MCU commands for your device. If an existing BASE device with the logic for your device is not available, a modified device driver will be required. This case is outside of the scope of this article.

    If your device is similar to the existing built-in module (e.g., a particular MCU or power monitoring algorithm) it is best to use that as a starting point. When you are not sure which module is suitable for your device, use Generic module (18).

    Finding Relays and Lights~

    Step 1.~

    Begin this procedure by disabling power state saves. Some improper GPIO assignments can cause device reboots. Disabling this setting avoids repeated flash writes. Also, it is best to allow Tasmota to return to a fail safe state in case of a bad configuration. Ensure that boot loop control is not disabled.

    Backlog SetOption0 0; SetOption36 1
    +

    Step 2.~

    Assign every available GPIO to successive Relay<x> components. For the initial GPIO probe, exclude "dedicated" GPIO such as GPIO0/GPIO2 and Tx/Rx, etc. You can use a Template to easily perform these assignments:

    {"NAME":"ID Relays","GPIO":[0,0,0,0,224,225,0,0,226,227,228,229,230,0],"FLAG":0,"BASE":18}
    +

    Save the configuration. Once the device reboots, use the virtual buttons on the web UI to find which of the assigned GPIO actually control the physical relays and LEDs on the device. Make note of which GPIO act on which device peripheral.

    Step 2a.~

    If you are unable to control some of the relays or LEDs on the device, they may be attached to the "dedicated" GPIO skipped in the initial probe. Now assign those GPIOs and repeat step 2a:

    {"NAME":"ID Relays 2","GPIO":[224,225,226,227,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
    +

    If your device is based on the ESP8285 and you are still unable to control some of the relays or LEDs on the device, they may be attached to GPIO9 or GPIO10. Now assign those remaining GPIO and repeat step 2a:

    {"NAME":"ID Relays 3","GPIO":[0,0,0,0,0,0,224,225,0,0,0,0,0,0],"FLAG":0,"BASE":18}
    +

    Step 2b.~

    Once you have found which GPIOs control the relays and LEDs, set these "active" GPIO to associate them with the corresponding Relay<x>, LED<x>, or LEDLink or PWM<x> (Some may require the use of inverted (i.e., Relay<x>i/LED<x>i/LEDLinki) component). Bulbs have mainly PWM.

    For proper operation, in the final device configuration, assignment of like components must begin from 1 and be assigned sequentially! Regular and inverted components can be intermixed (e.g., Relay1, then Relay2; Led1, then Led2i and so on).

    Buttons, Switches, nonPWM Lights or Power Monitoring~

    Step 1.~

    Now assign every remaining GPIO (excluding, once again, remaining "dedicated" GPIO like GPIO0/GPIO2 and Tx/Rx, etc.) to successive Switch1..Switch8 components (9..16). (If you have remaining GPIOs assign them as Counter)

    {"NAME":"ID Other","GPIO":[160,352,353,354,161,163,0,0,163,164,165,166,167,0],"FLAG":0,"BASE":18}
    +

    Step 2.~

    Save the configuration. Once the device reboots, use the web UI Console to run the Status 8 (sensors) command. This will display the current state of each GPIO. If you have a bulb, GPIOs which are in state ON will probably be SM16716 CLK or SM16716 DAT component.

    Step 3.~

    Run SetOption114 1 to show switch activations in console.

    If you have a power monitoring device, when under load the power monitoring chip should trigger switches or counters. Then it's just a matter of finding the right power monitoring components and their combination.

    Step 4.~

    Once you have found which GPIO are connected to each input, change the GPIO setting in the configuration to your input component or use case (f.e. Button<x> or Switch<x>). Proper operation may dictate the use of regular or inverted (i.e., Switch<x>i/Button<x>i) settings. For buttons, you may need to determine whether the internal pull-up is used or not. If so, select Button<x>n, where n indicates no pull-up.

    Step 1.~

    Begin this procedure by disabling power state saves. Some improper GPIO assignments can cause device reboots. Disabling this setting avoids repeated flash writes. Also, it is best to allow Tasmota to return to a fail safe state in case of a bad configuration. Ensure that boot loop control is not disabled.

    Backlog SetOption0 0; SetOption36 1
    +

    Step 2.~

    Assign every available GPIO to successive Relay<x> components. For the initial GPIO probe, exclude "dedicated" GPIO such as Tx/Rx, etc. You can use a Template to easily perform these assignments:

    {"NAME":"Find Relays","GPIO":[224,0,225,0,226,227,1,1,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,0,0,0,0,244,245,246,247,248,249,250,251],"FLAG":0,"BASE":1}
    +

    Save the configuration. Once the device reboots, use the virtual buttons on the web UI to find which of the assigned GPIO actually control the physical relays and LEDs on the device. Make note of which GPIO act on which device peripheral.

    Buttons, Switches, nonPWM Lights or Power Monitoring~

    Step 1.~

    Now assign every remaining available GPIO to successive Switch<x> components. For the initial GPIO probe, exclude "dedicated" GPIO such as Tx/Rx, etc. You can use a Template to easily perform these assignments:

    {"NAME":"Find Switches","GPIO":[160,0,161,0,162,163,1,1,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,0,0,0,0,180,181,182,183,184,185,186,187],"FLAG":0,"BASE":1,"CMND":"SO114 1 | SO65 0;"}
    +

    If you have a power monitoring device, when under load the power monitoring chip should trigger switches which will display in the console. Then its just a matter of finding the right power monitoring components and their combination.

    Step 4.~

    Once you have found which GPIO are connected to each input, change the GPIO setting in the configuration to your input component or use case (f.e. Button<x> or Switch<x>). Proper operation may dictate the use of regular, inverted or no pulldown settings. For buttons, you may need to determine whether the internal pull-up or pull-down is used or not.

    Finishing Configuration~

    Step 1~

    Once you have determined which GPIO your device uses, set any remaining GPIOs to None (0).

    Save the configuration.

    Once the device reboots, set the options change back to defaults with:

    Backlog SetOption0 1; SetOption114 0
    +

    Step 2.~

    Submit the new configuration: Since you have now configured a device not previously known to the Tasmota user base, you can export the template and submit it to the Tasmota Supported Devices Repository.

    Devices with TuyaMCU~

    In case your device is a Tuya device with an MCU which controls everything see TuyaMCU for instructions on how to configure it.

    Video for a tutorial on this procedure.

    \ No newline at end of file diff --git a/Contributing/index.html b/Contributing/index.html new file mode 100644 index 0000000000..679acc1299 --- /dev/null +++ b/Contributing/index.html @@ -0,0 +1 @@ + Contributing - Tasmota
    Skip to content

    Contributing

    Any contribution helps our team and makes Tasmota better for the entire community!

    You can contribute by:

    • providing Pull Requests (Features, Proof of Concepts, Language files or Fixes)
    • testing new released features and report issues
    • donating to acquire hardware for testing and implementing or out of gratitude
    • contributing documentation for features and devices
    • submitting new device templates
    • fixing spelling mistakes, broken links and other errors in the documentation
    • writing guides on wiring and using sensors or your DIY projects featuring Tasmota

    Editing articles~

    You need a GitHub user account to be able to edit

    If you spot an error in an article, use the pencil icon link (see image below) at the top of the related documentation GitHub repository page to correct it.

    This version of documentation uses strict markdown syntax with some enhancements. See Markdown Cheatsheet for all features.

    You'll be taken to the GitHub repository page of that article file, after a "fork" (copy) of the docs is made into your own GitHub account.

    Edit file

    Click on the edit button (pencil icon). Edit/Add the text you wanted. When finished find the Propose changes button at the bottom and click it.

    Propose file change

    Next you need to click Create pull request which is GitHub speak for propose changes. Create pull request

    In the PR window add a small description of what you did and click Create pull request Create pull request

    Now you wait for one of the Tasmota admins to approve your PR (or deny 😈, no guarantees!)

    Congratulations!

    You've made a contribution to the Tasmota project making it a better experience for all future users. Thank you!

    Adding articles~

    You need a GitHub user account to be able to edit

    If you want to add a new article, go to the documentation GitHub repository, click Add File and name the file with an .md extension.

    This version of documentation uses strict markdown syntax with some enhancements. See Markdown Cheatsheet for all features.

    You'll be taken to the GitHub repository page of that new article file, after a "fork" (copy) of the docs is made into your own GitHub account.

    Don't forget to add links to other article pages in the appropriate place that refer to the new article so that the new article can also be found.

    Adding device templates to the documentation~

    If you want to add a new device templates go to https://templates.blakadder.com/new.html (or its GitHub repository).

    \ No newline at end of file diff --git a/Control-a-Sonoff-using-a-remote-button/index.html b/Control-a-Sonoff-using-a-remote-button/index.html new file mode 100644 index 0000000000..4a8999fdae --- /dev/null +++ b/Control-a-Sonoff-using-a-remote-button/index.html @@ -0,0 +1 @@ + Control a Sonoff using a remote button - Tasmota
    Skip to content

    Control a Sonoff using a remote button

    REMOTE BUTTON~

    IMPORTANT: The ESP8266 needs GPIO0 and GPIO2 to be high (not connected to anything, or connected to 3.3V) during boot. GPIO0 and GPIO2 are the pins D3 and D4 on the Wemos D1 mini. D3 and D4 should only be used to connect push buttons (which are only low when pressed), not toggle switches.

    I have a cabin that is remote from my house, and I'm using a Sonoff to turn on/off the heaters remotely, so the place can be warm when I get there. The Sonoff(heater control) is located near the fuse box, and it controls a relay which switches the 120V/30A to the heaters.

    It's awkward to get at the Sonoff(heater control) to physically press the toggle button, so when I'm using it, I use MQTT commands to control the heaters. But I have some family members that are not comfortable with MQTT usage and certainly don't want to open the fuse box.

    So I built a small ESP-01 and a DHT22 and a push button, and used this project code to drive it (>rev 1.0.30). I use the cmnd/my_DHT/buttontopic to set the mqtt command to control the heaters. Yes, you can program a sonoff so that the button sends out any arbitrary command. In this case, I'm using the button to control the other sonoff. This ESP+DHT device is in the common area of the cabin, so my family members can simply push the button to toggle the heater.

    In other words, I'm using one trivial (DHT+button) wifi device to control the other (sonoff+heater relay). This technique uses Theo's flexible design, since buttontopic can be made different from topic.

    Schematic~

    schematic of trivial DHT + ESP01 NOTE: with this schematic, the DHT sensor is on GPIO2. You'll have to change the#define DHT_PIN in this project's source code from 14 to 2. Also, make sure you use the black ESP01 with 1MB of flash; most 2016 purchases should be ok. Of course you can use an ESP12 if you wish.

    The optional block shown attaches an LED to the DHT data line, so you can watch when the data is being fetched from the DHT device.

    Power regulator, 5V to 3.3V: search eBay for 5v/3.3v power 1117
    ESP: search eBay for esp-01 and pick a black one
    DHT22: search eBay for dht22 ; any AM2302, DHT11 or DHT12 will do in its place; or you can omit the DHT and just use the button
    Cost (2016-09-05) USB-5V-adapter: CAD$1 + DC5V->3.3V: CAD$1 + ESP01: CAD$2.62 + DHT12: CAD$1.50 = CAD$6.12 = EUR 4.25

    This little device has two inputs (DHT & button), and no output. You can use either the Sonoff or the ElectroDragon version of this project, but because this has no output, the following commands don't make any sense: power, light, ledstate. You may also want to change the #define APP_NAME and #define PROJECT to reflect that this isn't a real Sonoff.

    Pat B

    Update~

    2017-11-12~

    This design still works with version 5.5.2 of the firmware. There's no need anymore to create a special build; the new design allows you to customize the code at runtime.

    For the above schematic, go into the Config Module menu and set - Module: 18 WeMos D1 mini - GPIO0: 09 Switch1 - GPIO2: 03 DHT22

    and set switchtopic1 to the topic for the device you want to control, and switchmode1 to 3 (toggle). That's all you need to do :)

    I also discovered that the button must be a momentary pushbutton; don't use a switch, because both GPIO0 and GPIO2 must be floating at boot time.

    Another Update~

    2017-09-22~

    I have installed the above device at my cabin in the woods, and I'm using a cellular modem to connect to the internet for data. If I set the Tasmota-Sonoff software with a teleperiod=3600 (once per hour), the MQTT traffic is about 6kB/hour, which is about 4.2MB/month. That includes the TCP keep-alives and the telemetry messages. So two devices (the main sonoff power switch and the esp-01 button device) takes 8.4MB/month, just below my economy 10MB/month sim card.

    Just FYI, I also have tried using a Raspberry PI at the cabin, and instructing it to SSH to my public-facing server, and creating a reverse tunnel back so that I can access my cabin from my public-facing server. The keep-alives on that connection is about 7.6MB/month. [The cellular phone company is probably using NAT, so I have to connect from cabin->server and not the other way around]

    \ No newline at end of file diff --git a/Create-your-own-Firmware-Build-without-IDE/index.html b/Create-your-own-Firmware-Build-without-IDE/index.html new file mode 100644 index 0000000000..c8e6a00f1b --- /dev/null +++ b/Create-your-own-Firmware-Build-without-IDE/index.html @@ -0,0 +1,175 @@ + Create your own Firmware Build without IDE - Tasmota
    Skip to content

    Create your own Firmware Build without IDE

    PlatformIO is not just an IDE. In fact, all its features are accessible from the command line, and the IDE is a convenience wrapper layer around it.

    Thus, we can build Tasmota using only this PlatformIO-Core, which may come handy for automated builds, or for those who feel more comfortable with the command line than with the IDE.

    The steps are surprisingly simple and straightforward:

    Provision a Linux VM~

    At least if you want to work in a cloud environment, but you may also choose to work on your physical machine as well.

    PlatformIO is based on python, so if we use python-virtualenv, then all the dependent packages will be confined to a separate folder, so it won't even taint the OS installation.

    As all of python, python-virtualenv and python-pip are available in most of the recent distros, you may pick your favourite one.

    Install python and tools~

    Install python and python-virtualenv, and python-pip, because we don't want to mess up the python ecosystem of the distro.

    Update pip by pip install --upgrade pip, and this was the last step done as root, the rest goes as a plain user.

    I used CentOS here, so if you prefer Debian-based distros, just substitute apt-get install -y ... for yum install -y ....

    [tasmota_builder@jtest ~]$ sudo yum install -y python python-virtualenv python-pip
    +Loaded plugins: fastestmirror
    +Loading mirror speeds from cached hostfile
    +...
    +Complete!
    +
    You may update pip in the host environment, but we'll do it in the virtualenv as well, so it's optional:
    [tasmota_builder@jtest ~]$ sudo pip install --upgrade pip
    +Collecting pip
    +...
    +Successfully installed pip-18.1
    +

    Prepare a PlatformIO-Core environment contained in a folder~

    virtualenv creates a folder and prepares a whole self-contained python subsystem there.

    To activate it, so that all python-related things refer to this environment and not to the system global, you need to source the file bin/activate within it.

    NOTE: Not just execute in a subshell, but include it into the current one, so please note the . before bin/activate below:

    [tasmota_builder@jtest ~]$ virtualenv platformio-core
    +New python executable in /home/tasmota_builder/platformio-core/bin/python
    +Installing setuptools, pip, wheel...done.
    +[tasmota_builder@jtest ~]$ cd platformio-core
    +[tasmota_builder@jtest platformio-core]$ . bin/activate
    +
    +(platformio-core) [tasmota_builder@jtest platformio-core]$
    +

    Now we are ready to install PlatformIO-Core into this small virtual environment:

    (platformio-core) [tasmota_builder@jtest platformio-core]$ pip install -U platformio
    +Collecting platformio
    +  Downloading https://files.pythonhosted.org/packages/95/4a/3ccce45ba750dd9a8d48dcbe9b9080011ac2a5a248312b19552bbaec6b7d/platformio-3.6.3-py27-none-any.whl (160kB)
    +    100% |████████████████████████████████| 163kB 4.5MB/s 
    +Collecting semantic-version<3,>=2.5.0 (from platformio)
    +  Downloading https://files.pythonhosted.org/packages/72/83/f76958017f3094b072d8e3a72d25c3ed65f754cc607fdb6a7b33d84ab1d5/semantic_version-2.6.0.tar.gz
    +Collecting click<6,>=5 (from platformio)
    +  Downloading https://files.pythonhosted.org/packages/8f/98/14966b6d772fd5fba1eb3bb34a62a7f736d609572493397cdc5715c14514/click-5.1-py2.py3-none-any.whl (65kB)
    +    100% |████████████████████████████████| 71kB 8.1MB/s 
    +Collecting colorama (from platformio)
    +  Downloading https://files.pythonhosted.org/packages/4f/a6/728666f39bfff1719fc94c481890b2106837da9318031f71a8424b662e12/colorama-0.4.1-py2.py3-none-any.whl
    +Collecting requests<3,>=2.4.0 (from platformio)
    +  Downloading https://files.pythonhosted.org/packages/7d/e3/20f3d364d6c8e5d2353c72a67778eb189176f08e873c9900e10c0287b84b/requests-2.21.0-py2.py3-none-any.whl (57kB)
    +    100% |████████████████████████████████| 61kB 7.9MB/s 
    +Collecting pyserial!=3.3,<4,>=3 (from platformio)
    +  Downloading https://files.pythonhosted.org/packages/0d/e4/2a744dd9e3be04a0c0907414e2a01a7c88bb3915cbe3c8cc06e209f59c30/pyserial-3.4-py2.py3-none-any.whl (193kB)
    +    100% |████████████████████████████████| 194kB 4.7MB/s 
    +Collecting bottle<0.13 (from platformio)
    +  Downloading https://files.pythonhosted.org/packages/32/4e/ed046324d5ec980c252987c1dca191e001b9f06ceffaebf037eef469937c/bottle-0.12.16.tar.gz (72kB)
    +    100% |████████████████████████████████| 81kB 8.8MB/s 
    +Collecting urllib3<1.25,>=1.21.1 (from requests<3,>=2.4.0->platformio)
    +  Downloading https://files.pythonhosted.org/packages/62/00/ee1d7de624db8ba7090d1226aebefab96a2c71cd5cfa7629d6ad3f61b79e/urllib3-1.24.1-py2.py3-none-any.whl (118kB)
    +    100% |████████████████████████████████| 122kB 7.5MB/s 
    +Collecting chardet<3.1.0,>=3.0.2 (from requests<3,>=2.4.0->platformio)
    +  Downloading https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl (133kB)
    +    100% |████████████████████████████████| 143kB 6.8MB/s 
    +Collecting idna<2.9,>=2.5 (from requests<3,>=2.4.0->platformio)
    +  Downloading https://files.pythonhosted.org/packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-py2.py3-none-any.whl (58kB)
    +    100% |████████████████████████████████| 61kB 7.8MB/s 
    +Collecting certifi>=2017.4.17 (from requests<3,>=2.4.0->platformio)
    +  Downloading https://files.pythonhosted.org/packages/9f/e0/accfc1b56b57e9750eba272e24c4dddeac86852c2bebd1236674d7887e8a/certifi-2018.11.29-py2.py3-none-any.whl (154kB)
    +    100% |████████████████████████████████| 163kB 5.9MB/s 
    +Building wheels for collected packages: semantic-version, bottle
    +  Running setup.py bdist_wheel for semantic-version ... done
    +  Stored in directory: /home/tasmota_builder/.cache/pip/wheels/60/bb/50/215d669d31f992767f5dd8d3c974e79261707ee7f898f0dc10
    +  Running setup.py bdist_wheel for bottle ... done
    +  Stored in directory: /home/tasmota_builder/.cache/pip/wheels/0c/68/ac/1546dcb27101ca6c4e50c5b5da92dbd3307f07cda5d88e81c7
    +Successfully built semantic-version bottle
    +Installing collected packages: semantic-version, click, colorama, urllib3, chardet, idna, certifi, requests, pyserial, bottle, platformio
    +Successfully installed bottle-0.12.16 certifi-2018.11.29 chardet-3.0.4 click-5.1 colorama-0.4.1 idna-2.8 platformio-3.6.3 pyserial-3.4 requests-2.21.0 semantic-version>
    +You are using pip version 9.0.1, however version 18.1 is available.
    +
    As it would prefer a recent pip instead of the one set up by virtualenv, so let's upgrade it:

    (platformio-core) [tasmota_builder@jtest platformio-core]$ pip install --upgrade pip
    +Collecting pip
    +  Downloading https://files.pythonhosted.org/packages/c2/d7/90f34cb0d83a6c5631cf71dfe64cc1054598c843a92b400e55675cc2ac37/pip-18.1-py2.py3-none-any.whl (1.3MB)
    +    100% |████████████████████████████████| 1.3MB 793kB/s 
    +Installing collected packages: pip
    +  Found existing installation: pip 9.0.1
    +    Uninstalling pip-9.0.1:
    +      Successfully uninstalled pip-9.0.1
    +Successfully installed pip-18.1
    +

    Fetch the Tasmota sources~

    If you want only to build, then the original repo will do, but if you want to contribute as well, then fork an own copy of the repo and clone out that one.

    (platformio-core) [tasmota_builder@jtest platformio-core]$ git clone https://github.com/arendst/Tasmota.git
    +Cloning into 'Tasmota'...
    +remote: Enumerating objects: 6, done.
    +remote: Counting objects: 100% (6/6), done.
    +remote: Compressing objects: 100% (5/5), done.
    +remote: Total 16930 (delta 1), reused 3 (delta 1), pack-reused 16924
    +Receiving objects: 100% (16930/16930), 23.75 MiB | 12.94 MiB/s, done.
    +Resolving deltas: 100% (11426/11426), done.
    +

    After changing to the working copy, we are ready to go:

    (platformio-core) [tasmota_builder@jtest platformio-core]$ cd Tasmota/
    +(platformio-core) [tasmota_builder@jtest Tasmota]$
    +

    Configure the sources~

    Now you may want to configure the sources for your needs.

    Actually, the sources do build fine right out-of-the box, only it'll be a full build, including all the language localisation and all the build flavours as well, while you are usually interested only in one language and one build flavour only.

    In tasmota/user_config_override.h fine tune the default values for the module, the wifi, the MQTT server, and so on.

    Build the firmware~

    The build command itself is pio run, but as it emits quite a lot of messages (including errors if you're developing), so you may want to redirect a copy of the standard output and error to a file, so it'll be pio run 2>&1 | tee build.log.

    (platformio-core) [tasmota_builder@jtest Tasmota]$ time pio run 2>&1 | tee build.log
    +************************************************************************************************************************************************************************
    +If you like PlatformIO, please:
    +- follow us on Twitter to stay up-to-date on the latest project news > https://twitter.com/PlatformIO_Org
    +- star it on GitHub > https://github.com/platformio/platformio
    +- try PlatformIO IDE for IoT development > https://platformio.org/platformio-ide
    +- support us with PlatformIO Plus > https://pioplus.com
    +************************************************************************************************************************************************************************
    +
    +Processing tasmota (framework: arduino; platform: espressif8266@1.8.0; board: esp01_1m)
    +------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    +PlatformManager: Installing espressif8266 @ 1.8.0
    +Downloading
    +...
    +Environment tasmota-TW           [SUCCESS]
    +Environment tasmota-UK           [SUCCESS]
    +==================================================================== [SUCCESS] Took 797.56 seconds ====================================================================
    +

    That's all, really :D !

    PlatformIO seems to handle the rebuilds and dependencies well, but if you want a clean build, the say pio run -t clean first, and then the pio run.

    Collect the results~

    The results will be here: build_output

    About build times~

    The recent versions of PlatformIO-Core seem to parallelise quite well.

    When you've changed only a few files, not everything needs to be recompiled (though the image must still be re-packed), so that minute-like build time is the maximum, usually it'll be less.

    Prepare the local installer tool~

    You may rebuild the firmware on a remote machine, but you must have the installer tool on the local machine where the module is connected to.

    Fortunately, it's also python-based, so we can again employ virtualenv here.

    If you built the firmware also on your localhost, then there is no need for a separate environment, you may quite well install esptool into that one.

    Otherwise, create a virtual environment the usual way:

    [tasmota_installer@lantash ~]$ virtualenv esptool
    +New python executable in /home/tasmota_installer/esptool/bin/python2.7
    +Also creating executable in /home/tasmota_installer/esptool/bin/python
    +Installing setuptools, pip, wheel...done.
    +
    +[tasmota_installer@lantash ~]$ cd esptool/
    +
    +[tasmota_installer@lantash ~/esptool]$ . bin/activate
    +
    +(esptool) [tasmota_installer@lantash ~/esptool]$ pip install --upgrade pip
    +Requirement already up-to-date: pip in ./lib/python2.7/site-packages (18.1)
    +

    Now let's install esptool:

    (esptool) [tasmota_installer@lantash ~/esptool]$ pip install esptool
    +Collecting esptool
    +  Downloading https://files.pythonhosted.org/packages/5c/85/5654e7b9019739d3d89af0adf528c9ae57a9a26682e3aa012e1e30f20674/esptool-2.6.tar.gz (80kB)
    +    100% |################################| 81kB 222kB/s 
    +Collecting pyserial>=3.0 (from esptool)
    +  Downloading https://files.pythonhosted.org/packages/0d/e4/2a744dd9e3be04a0c0907414e2a01a7c88bb3915cbe3c8cc06e209f59c30/pyserial-3.4-py2.py3-none-any.whl (193kB)
    +    100% |################################| 194kB 491kB/s 
    +Collecting pyaes (from esptool)
    +  Downloading https://files.pythonhosted.org/packages/44/66/2c17bae31c906613795711fc78045c285048168919ace2220daa372c7d72/pyaes-1.6.1.tar.gz
    +Collecting ecdsa (from esptool)
    +  Downloading https://files.pythonhosted.org/packages/63/f4/73669d51825516ce8c43b816c0a6b64cd6eb71d08b99820c00792cb42222/ecdsa-0.13-py2.py3-none-any.whl (86kB)
    +    100% |################################| 92kB 382kB/s 
    +Building wheels for collected packages: esptool, pyaes
    +  Running setup.py bdist_wheel for esptool ... done
    +  Stored in directory: /home/tasmota_installer/.cache/pip/wheels/cf/1f/62/7ad4e47843affd4f5b7032a39f1ef8a153c6d27533614d21aa
    +  Running setup.py bdist_wheel for pyaes ... done
    +  Stored in directory: /home/tasmota_installer/.cache/pip/wheels/bd/cf/7b/ced9e8f28c50ed666728e8ab178ffedeb9d06f6a10f85d6432
    +Successfully built esptool pyaes
    +Installing collected packages: pyserial, pyaes, ecdsa, esptool
    +Successfully installed ecdsa-0.13 esptool-2.6 pyaes-1.6.1 pyserial-3.4
    +

    If you've built the firmware on a remote machine, now it's time to download it into this installer environment (e.g. via scp or sftp).

    IMPORTANT: For the subsequent steps your user must have the permission to write the serial port.

    Back up the current firmware (optional)~

    First of all, disconnect the bulb from the mains and wire up the serial connection and a button on GPIO0.

    If this GPIO0 is connected to GND when the module gets power, it starts in a firmware-update mode, and you can then read/write its flash storage.

    Switch off the power of the board, this will be the reference 'steady state' of the system.

    (esptool) [tasmota_installer@lantash ~/esptool]$ esptool.py read_flash 0x00000 0x100000 fcmila_bulb_orig.bin
    +esptool.py v2.6
    +Found 1 serial ports
    +Serial port /dev/cuaU0
    +Connecting......
    +

    Now, it'll wait for the module to appear connected, so

    • press the button (GPIO0 to GND), keep it pressed
    • switch on the power of the board
    • now you may release the button
    ...
    +Detecting chip type... ESP8266
    +Chip is ESP8266EX
    +Features: WiFi
    +MAC: bc:dd:c2:e0:2a:f2
    +Uploading stub...
    +Running stub...
    +Stub running...
    +1048576 (100 %)
    +Read 1048576 bytes at 0x0 in 95.0 seconds (88.3 kbit/s)...
    +Hard resetting via RTS pin...
    +

    If all is well, the flash is being dumped, it may take a minute or so.

    If done, then power the module off, as this management mode is not restartable!

    If it's not well, then you may try some queries:

    (esptool) [tasmota_installer@lantash ~/esptool]$ esptool.py -p /dev/ttyU0 chip_id
    +...
    +Chip ID: 0x00e02af2
    +...
    +(esptool) [tasmota_installer@lantash ~/esptool]$ esptool.py -p /dev/ttyU0 flash_id
    +...
    +Manufacturer: c8
    +Device: 4014
    +Detected flash size: 1MB
    +...
    +

    If they don't work, then check your cabling and your serial adapter. Until you can't get this step working, don't proceed to the next one, it won't work either.

    Erase the flash~

    (With the usual button-pressed-power-on rain dance, and don't forget to power the module off afterwards.)

    (esptool) [tasmota_installer@lantash ~/esptool]$ esptool.py erase_flash
    +...
    +Erasing flash (this may take a while)...
    +Chip erase completed successfully in 1.6s
    +

    Install the firmware to your module~

    (esptool) [tasmota_installer@lantash ~/esptool]$ esptool.py write_flash --flash_size 1MB --flash_mode dout 0x00000 firmware.bin
    +Configuring flash size...
    +Compressed 535424 bytes to 367679...
    +Wrote 535424 bytes (367679 compressed) at 0x00000000 in 33.8 seconds (effective 126.6 kbit/s)...
    +Hash of data verified.
    +
    +Leaving...
    +

    Power on for normal operation~

    No button-pressing, power on, and see what you achieved :).

    The module sends its logs on the serial line at 115200 baud 8N1, so to check the logs:

    (esptool) [tasmota_installer@lantash ~/esptool]$ cu -s 115200 -l /dev/ttyU0 | tee -a my_sonoff.log
    +Connected
    +<some initial binary data>
    +00:00:00 CFG: Use defaults
    +00:00:00 SM16716: ModuleSelected; clk_pin=4, dat_pin=14)
    +00:00:00 SRC: Restart
    +00:00:00 SM16716: Entry; function=FUNC_SET_DEVICE_POWER, index=00, payload=02
    +00:00:00 SM16716: Update; pwr=00, rgb=000000
    +00:00:00 Project sonoff Sonoff Version 6.4.1.9(sonoff)-2_4_2
    +00:00:00 SM16716: Entry; function=FUNC_INIT
    +00:00:00 SM16716: ModuleSelected; clk_pin=4, dat_pin=14)
    +00:00:00 WIF: Attempting connection...
    +...
    +

    (Assuming that you're using FreeBSD. On Linux you set the speed via setserial or stty, and then do the dump with dd. Or just minicom, if you prefer.)

    Now you have a complete build path from source to device, and a log feedback as well, so you've got everything needed for being able to implement your ideas :D !

    \ No newline at end of file diff --git a/DFR0299/index.html b/DFR0299/index.html new file mode 100644 index 0000000000..ee2df7c928 --- /dev/null +++ b/DFR0299/index.html @@ -0,0 +1,5 @@ + DFRobot DFPlayer Mini MP3 Player - Tasmota
    Skip to content

    DFRobot DFPlayer Mini MP3 Player~

    This feature is included only in tasmota-sensors and tasmota32 binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_MP3_PLAYER 
    +#define USE_MP3_PLAYER                  // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, pause, stop, track, volume and reset
    +    #define MP3_VOLUME           30     // Set the startup volume on init, the range can be 0..100(max)
    +#endif
    +

    Small and low price MP3 module with a simplified output directly to the speaker

    See manufacturer site for more information.

    Configuration~

    Wiring~

    DFPlayer ESP
    GND GND
    5V 5V
    RX GPIO

    Tasmota Settings~

    In the Configuration -> Configure Module page assign: 1. GPIO RX to MP3 Player

    Available commands

    Pinout Wiring

    \ No newline at end of file diff --git a/DHT11-Wiring---Sonoff-Basic/index.html b/DHT11-Wiring---Sonoff-Basic/index.html new file mode 100644 index 0000000000..e4eac37402 --- /dev/null +++ b/DHT11-Wiring---Sonoff-Basic/index.html @@ -0,0 +1 @@ + DHT11 Wiring Sonoff Basic - Tasmota

    DHT11 Wiring Sonoff Basic

    To wire a DHT11 up to a Sonoff Basic, I first soldered a pin header to the main pin header row. For the location see [[Pinouts]].

    I decided to use regular 2.54mm connectors (often referred to as Dupont connectors), so that I can switch sensors if I have to. In order to fit the dupont cables within the enclosure, I had to bend the 2.54mm pin headers by about 45°.

    As the DHT11 requires a pullup (depending on the cable length), and I didn't want to design a PCB just to connect 3 wires and a resistor, I came up with the solution described below.

    First, I slipped ferrules over the DHT11 pins and inserted the wires. This makes it a lot easier to hold the cables and the DHT11 pins in place.

    I left a bit more wire exposed so that I have a place to apply solder without burning the wire insulation. I applied heat to the ferrule, not the wire, and applied solder directly at the end of the ferrule.

    Solder will then flow into the ferrule.

    I also added a small solder blob (visible on the top ferrule) so that I can solder an 0805 resistor in place. Thanks to the ferrule, the wires won't become detached when heated again. Keep in mind that the ferrule has quite some heat capacity, so wait for the solder to cool down prior moving the wires or the sensor.

    I then did shrink the 2:1 green heatshrink tubes so that the contacts don't touch each other.

    Also, it is so very handy to have 4:1 shrinking tube. An 8mm 4:1 tube fits perfectly over the pins, and fully encloses the 3 wires.

    Get 4:1 heatshrink tube. It's so worth it.

    Here's how the finished project looks like:

    \ No newline at end of file diff --git a/DHT11/index.html b/DHT11/index.html new file mode 100644 index 0000000000..eb17b5d896 --- /dev/null +++ b/DHT11/index.html @@ -0,0 +1,25 @@ + DHT11 temperature and humidity sensor - Tasmota
    Skip to content

    DHT11 temperature and humidity sensor~

    This feature is included in tasmota, tasmota32, tasmota-knx and tasmota-display binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_DHT
    +#define USE_DHT     // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor (1k6 code)
    +#endif
    +

    DHT11 is a basic, ultra low-cost digital temperature and humidity sensor. It is very inaccurate and surpassed by others (AM2301, BME280, ...) thus not recommended by the Tasmota team.

    Configuration~

    Wiring~

    DHT11 ESP
    - GND
    OUT GPIOx
    + 3.3V

    Tasmota Settings~

    In the Configuration -> Configure Module page assign:

    • GPIOx to DHT11

    After reboot of the device the temperature and humidity are displayed.

    Commands~

    TempOffset can be used for calibrating the measured temperature. This setting affects all temperature sensors on the device.

    Wemos DHT11 Shield~

    Note: this is ONLY for v1.0.0 of the DHT11 shield, since v2.0.0 uses I2C and pinned differently.

    From the Wemos DHT11 shield specs the DATA OUT pin is connected to D4 of the Wemos.

    Note: Having the shield compact on top of the processor increases the temperature. In normal Mode this can be up to 6°C. So you should add sleep mode. Adding normal "Sleep 100" and putting the sensor vertically reduces the temperature difference to 2°C

    Sonoff Basic specific~

    To wire a DHT11 up to a Sonoff Basic, I first soldered a pin header to the main pin header row. For the location see GPIO Locations.

    I decided to use regular 2.54mm connectors (often referred to as Dupont connectors), so that I can switch sensors if I have to. In order to fit the dupont cables within the enclosure, I had to bend the 2.54mm pin headers by about 45°.

    As the DHT11 requires a pullup (depending on the cable length), and I didn't want to design a PCB just to connect 3 wires and a resistor, I came up with the solution described below.

    First, I slipped ferrules over the DHT11 pins and inserted the wires. This makes it a lot easier to hold the cables and the DHT11 pins in place.

    I left a bit more wire exposed so that I have a place to apply solder without burning the wire insulation. I applied heat to the ferrule, not the wire, and applied solder directly at the end of the ferrule.

    Solder will then flow into the ferrule.

    I also added a small solder blob (visible on the top ferrule) so that I can solder an 0805 resistor in place. Thanks to the ferrule, the wires won't become detached when heated again. Keep in mind that the ferrule has quite some heat capacity, so wait for the solder to cool down prior moving the wires or the sensor.

    I then did shrink the 2:1 green heatshrink tubes so that the contacts don't touch each other.

    Also, it is so very handy to have 4:1 shrinking tube. An 8mm 4:1 tube fits perfectly over the pins, and fully encloses the 3 wires.

    Get 4:1 heatshrink tube. It's so worth it.

    Here's how the finished project looks like:

    OpenHab~

    sonoff.items:

     // DHT-6
    + Number DHT6_Temp "Bathroom DHT-6 [%.1f °C]"
    +            <temperature>          (gTemperature,gTemperatureRoom)
    +            { mqtt="<[broker:tele/sonoff-dht-6/SENSOR:state:JSONPATH($.DHT11.Temperature)]" }
    + Number DHT6_Humidity "Bathroom DHT-6 [%.1f %%]"
    +            <humidity>             (gHumidity)       
    +            { mqtt="<[broker:tele/sonoff-dht-6/SENSOR:state:JSONPATH($.DHT11.Humidity)]" }
    + Switch DHT6_Reachable "DHT-6 reachable"
    +            <contact>              (gReachable)      
    +            { mqtt="<[broker:tele/sonoff-dht-6/LWT:state:MAP(reachable.map)]" }
    + Number DHT6_RSSI "DHT-6 RSSI [%d %%]"
    +            <qualityofservice>     (gRSSI)
    +            { mqtt="<[broker:tele/sonoff-dht-6/STATE:state:JSONPATH($.Wifi.RSSI)]" }
    +

    sonoff.sitemap

     Frame {
    +              Text item=DHT6_Temp  labelcolor=[DHT6_Reachable == "ON" = "green",DHT6_Reachable == "OFF" = "red"] {
    +                   Text item=DHT6_Temp
    +                   Text item=DHT6_Humidity
    +                   Text item=DHT6_Reachable
    +                   Text item=DHT6_RSSI
    +                   }
    +        }
    +

    Printable Housing~

    https://www.thingiverse.com/thing:2814909

    \ No newline at end of file diff --git a/DS18x20/index.html b/DS18x20/index.html new file mode 100644 index 0000000000..c6c3b53404 --- /dev/null +++ b/DS18x20/index.html @@ -0,0 +1,18 @@ + DS18x20 temperature sensor - Tasmota
    Skip to content

    DS18x20 temperature sensor~

    This feature is included in tasmota, tasmota32, tasmota-knx, tasmota-display and tasmota32-bluetooth binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_DS18x20
    +#define USE_DS18x20               // Add support for DS18x20 sensors with id sort, single scan and read retry (+2k6 code)
    +//  #define W1_PARASITE_POWER     // Optimize for parasite powered sensors
    +#endif
    +

    DS18x20 driver supports DS18B20, DS18S20, DS1822 and MAX31850 1-Wire digital temperature sensors.

    Configuration~

    Wiring~

    DS18b20 ESP
    1 GND GND
    2 Data GPIOx
    3 VCC 3.3v

    Warning: DS18x20 will not work on GPIO16

    You need to connect a 4.7K pullup resistor from data to 3.3V.
    Wiring

    Tasmota Settings~

    In the Configuration -> Configure Module page assign:

    GPIOx to DS18x20

    After a reboot the driver will display Temperature.

    Shelly 1 Temperature Addon~

    Shelly dual pin mode is supported by an additional pin assignment:

    • GPIOy to DS18x20-o

    Commands~

    SetOption74 may be used to enable/disable internal pullup when using a single DS18x20 sensor (for multiple sensors, you have to use an external pullup resistor).

    SetOption126 Enable arithmetic mean over teleperiod for JSON temperature for DS18x20 sensors.

    TempRes Temperature sensor resolution: 0..3 = maximum number of decimal places.

    TempOffset can be used to add/sub an offset to the measured temperature. This setting affects all temperature sensors on the device.

    Multiple Sensors~

    Tasmota supports multiple DS18x20 sensors connected to a single ESP8266/ESP32 chip using a single or multiple (up to 4) GPIOs. The default maximum is set to 8 sensors (driver code). It is possible to override this number in user_config_override.h by adding a line with #define DS18X20_MAX_SENSORS <new-value> (ESP8266/ESP32 only). However one should take into account that:

    • Display on the console is limited and SENSOR log will be truncated above 11 DS18x20.
    • MQTT buffer length is limited and SENSOR message will be truncated above 18 DS18x20.
    • Even less if other sensors are attached to the ESP device and present in the SENSOR message.
    • 1-wire has been designed to be a board-bus, not to run through long distances across a whole house. At minimum, use a shielded cable.

    Note

    If you increase the value above default, you are on your own. No support will be provided.

    multiple wiring

    Every sensors will get a unique ID used in webUI and MQTT telemetry.

    {"Time":"2021-01-02T09:09:44","DS18B20-1":{"Id":"00000566CC39","Temperature":13.3},"DS18B20-2":{"Id":"0000059352D4","Temperature":1.2},"DS18B20-3":{"Id":"000005937C90","Temperature":22.5},"TempUnit":"C"}
    +

    Sensor Aliases~

    By default, sensors are named using the <sensor model>-<n> scheme (e.g. DS18B20-1), where the number is practically random (based on the sensor's address). This means when you add or remove a sensor, the numbers of the sensors may change.

    To assign a static alias to a certain ID, you can use DS18Alias command. This must be enabled by setting #define DS18x20_USE_ID_ALIAS in user_config_override.h and then compile your own binary.

    To display the full IDs of all connected sensors, run the command DS18Alias without any parameters.
    To set a static index, use the DS18Alias <sensor ID>,<n> command. For example DS18Alias 113C01F096174528,2. This will rename the sensor to the scheme DS18Sens-<n> and it leads to the following output of the command DS18Alias:

    {"DS18Sens-2":{"Id":"113C01F096174528"},"DS18Sens-1":{"Id":"783C01F096F2BB28"},"DS18Sens-3":{"Id":"8B0316C310F3FF28"}}
    +

    When you set <n> to an alphanumeric value (not starting with a number), the whole name is replaced with the given value. E.g. DS18Alias 113C01F096174528,Outdoor

    As this settings are not persistent across reboots, you should add a System#Boot rule (or an entry in autoexec.bat). This will set your aliases again after each boot. For example:

    Rule1 ON System#Boot DO
    +Backlog DS18Alias 783C01F096F2BB28,1;
    +DS18Alias 113C01F096174528,2;
    +DS18Alias 8B0316C310F3FF28,3
    +ENDON
    +

    To remove the alias and return to the default naming scheme, use 0 as the number index: DS18Alias <sensor ID>,0
    For example: DS18Alias 113C01F096174528,0

    Compile Options~

    There are some compile options (driver code):

    USE_DS18x20_RECONFIGURE: When sensor is lost keep retrying or re-configure
    DS18x20_USE_ID_AS_NAME: Use last 3 bytes for naming of sensors
    DS18x20_USE_ID_ALIAS: Enable DS18Alias command (see above)

    Sensor Versions~

    Sensor variations

    Pinout when looking at the flat side of the TO-92 package sensor: GND, Signal, VDD. Pinout of the wired sensor: black: GND; yellow or white: Signal, red: VDD

    THR316D/THR320D Wiring Notes~

    4P4C RJ9/RJ10/RJ22

    4P4C RJ9/RJ10/RJ22 ESP DS18b20
    1 (Yellow on image) 3V3 GPIO27 providing 3V3 Red on wired sensor
    2 (Green on image) GPIO25 data Yellow or White on wired sensor
    3 (Red on image) NC
    4 (Black on image) GND Black on wired sensor

    You do not need to add a pull-up resistor (The THR316D/THR320D has it built in.)

    ESP-01 Wiring Notes~

    Danger

    BEWARE OF MANY VISUALLY SIMILAR BOARDS TO THIS RELAY BOARD but different schematics

    ESP-01S

    Some modules have culprits: * "CH_PD" is not set to HI (3.3V) as actually required. Usually this is done with a 10K resistor or directly to 3.3V, I have connected directly to the 3.3V * A resistor (R2 10k) which is connected between the terminal GPIO0 to ground. This ensures that the GPIO0 is always pulled to ground, which actually places the ESP-01 in program mode (flashing). To make the module working it is necessary to remove (solder out).

    Connect DS18B20 to the GPIO2 (see diagram below - soldering not necessary, it is possible to put the wires and the resistor directly in to the female part of the connector together with ESP 01S module pins)

    After flashing, set up Tasmota (see images below): * "Generic module" * GPIO0 as Relay 1 * GPIO2 as DS18x20

    Rule Triggers~

    The following trigger events are supported for use in Rules:

    Single sensor attached:

      ON DS1820#Temperature DO <command> ENDON
    +
    Multiple sensors attached:
      ON DS1820_1#Temperature DO <command> ENDON
    +  ON DS1820_2#Temperature DO <command> ENDON
    +  ON DS1820_3#Temperature DO <command> ENDON
    +  ON DS1820_..#Temperature DO <command> ENDON
    +
    Example:
     ON DS1820_1#Temperature!=%Var1% DO backlog publish espTempertature/sensor/DS1820_1/data %value%; Var1 %value% ENDON
    +

    \ No newline at end of file diff --git a/DS3231/index.html b/DS3231/index.html new file mode 100644 index 0000000000..95dbd388b9 --- /dev/null +++ b/DS3231/index.html @@ -0,0 +1,8 @@ + DS3231 Real Time Clock - Tasmota
    Skip to content

    DS3231 Real Time Clock~

    This feature is not included in precompiled binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_RTC_CHIPS 
    +#define USE_RTC_CHIPS               // Enable RTC chip support and NTP server
    +#endif
    +#ifndef USE_DS3231
    +#define USE_DS3231                  // [I2cDriver26] Enable DS3231 RTC (I2C address 0x68) (+1k2 code)
    +#endif
    +
    If you use a DS3231 with a different I2C address, uncomment and change your address with:
    #define USE_RTC_ADDR    0x68                  
    +

    Tasmota requires that its system time is set in order to use timers. Usually, when the device boots, it gets the time and date from an an NTP (Network Time Protocol) server located somewhere on the Internet. When there is no Internet connection, Tasmota is not able to request the current date and time. The DS3231 is an external Real Time Clock (RTC) component used for keeping the time and date set in the device when it cannot establish an Internet connection.

    Optionally, the Time command is available to manually set date and time for the real time clock. The DS3231 will evaluate this time and sync to it if date is later than Jan 1, 2016. Note that the Time command will disable NTP sync, and the real time clock will drift based on the accuracy of the DS3231 module.

    The RTC driver optionally includes a simple NTP server enabled by #define RTC_NTP_SERVER. It will respond to NTP requests on the standard port 123 as a stratum 1 server. This is useful as a backup time source for network devices if internet access is unavailable. NTP server can be activated with the command RtcNtpServer 1 and deactivated with RtcNtpServer 0

    Configuration~

    Wiring~

    DS3231 ESP
    GND GND
    VCC 3.3V
    SDA GPIOx
    SCL GPIOy

    Tasmota Settings~

    In the Configuration -> Configure Module page assign:

    1. GPIOx to I2C SDA
    2. GPIOy to I2C SCL

    Wiring on D1 mini~

    Tasmota Settings~

    Any time your device has an NTP connection, the DS3231 internal clock will be set automatically. The first time RTC is used, you must have an Internet connection. From that point forward, the time is stored in the RTC and the device time will be restored from the RTC when there is no Internet connection.

    You can use your mobile phone hotspot for your device in a location where it can't get a Wi-Fi connection

    Check that the time is set correctly by inspecting the log in the Console. You should see messages that Tasmota read or wrote the time from and to the DS3231. Make sure that the time is set correctly even when you do not have Internet connection.

    \ No newline at end of file diff --git a/DY-SV17F/index.html b/DY-SV17F/index.html new file mode 100644 index 0000000000..51148ce37d --- /dev/null +++ b/DY-SV17F/index.html @@ -0,0 +1,7 @@ + DY-SV17F MP3 Player module - Tasmota
    Skip to content

    DY-SV17F MP3 Player module~

    This feature is included only in tasmota-sensors and tasmota32 binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_DY_SV17F
    +#define USE_DY_SV17F                   // Use of DY-SV17F MP3 Player commands: play, stop, track and volume
    +    #define MP3_VOLUME           30    // Set the startup volume on init, the range can be 0..100(max)
    +#endif
    +

    The DY-SV17F MP3 Player module has 4MBytes of internal flash storage for audio files you can upload via USB. An onboard 5W amplifier can power 4, 3-5W speakers

    Configuration~

    Wiring~

    DY-SV17F ESP
    GND GND
    5V 5V
    RX GPIO

    Commands~

    Command Parameters
    MP3Play Play, works as a normal play on a real MP3 Player, starts at first MP3 file
    MP3Stop Stop
    MP3Track x = play track <x>
    MP3Volume 0..100 = set Volume

    Pinout

    Datasheet

    \ No newline at end of file diff --git a/DeepSleep/index.html b/DeepSleep/index.html new file mode 100644 index 0000000000..ffdf8d3d5c --- /dev/null +++ b/DeepSleep/index.html @@ -0,0 +1,51 @@ + DeepSleep - Tasmota
    Skip to content

    DeepSleep

    DeepSleep support for up to 10 years (i.e., 86,400 seconds = 1 day) (e.g., if used with KNX) (DeepSleepTime).

    DeepSleepTime sets the time the device remains in DeepSleep before it returns to full operating mode. Once the command is issued, the DeepSleep cycle commences. During DeepSleep, the device is effectively off and, as such, it is not possible to modify DeepSleepTime without exiting DeepSleep.

    Example

    With DeepSleepTime 3600, the device will wake up exactly every hour (e.g., 8:00am, 9:00am, ...). If you define DeepSleepTime 86400 (i.e., 60*60*24), it will wake-up exactly at 0:00 local time. There is no option to shift the wakeup time; except changing the timezone. If you define DeepSleepTime 600, it will wake-up every 10 minutes (e.g., 8:00, 8:10, 8:20, ...).

    Note

    The next wake time will always be an even number of DeepSleepTime cycles since the epoch (Midnight 1970-01-01). This may matter if the sleep time isn't an even number of minutes/hours (ex: 3660), such as when trying to wake at a specific time of day.

    In order for the device to wake itself to perform its function during the DeepSleep cycle, the RST pin must be connected to the D0/GPIO16 pin. This is the only pin which can perform this function as the wake-up signal is sent from the RTC through D0/GPIO16 to RST. When connected to RST for DeepSleep purpose, GPIO16 may not be used for other functions. As such, it is recommended to leave it configured as None (0). On the diagram, black denotes existing parts and connections on a standard ESP board (mini-D1, NodeMCU, ...). Red denotes what is added to the DeepSleep feature.

    Methods to (temporarily) disable DeepSleep mode~

    Using a switch~

    Select another GPIO (let's call it "GPIOn") and connect it GND. This can be performed through a switch per the schematic below. Flipping the switch to "ON" will prevent Tasmota to enter DeepSleep again after next wake-up until the switch is flipped back OFF. On the diagram, blue denotes additional parts and connections to be able to disableDeepSleep. GPIOn should be defined as DeepSleep (182) in the configuration as shown below:

    The following GPIOs CANNOT be used for the purpose of temporarily disabling DeepSleep as described above:
    - GPIO16 (because it is connected to RST),
    - GPIO15 (because of an existing on-board pull-down resistor),
    - GPIO0 (because pulling it down at wake up will enter serial bootload mode).

    All others GPIO should be acceptable.

    An interesting use-case is to disable DeepSleep when external power (USB, PSU, solar panel...) is applied to the device using a transistor.

    DeepSleep and transistor

    If the device is not (easily) accessible, methods can be used to disable the DeepSleep loop without physical access.

    Using MQTT~

    Send a retained DeepSleepTime 0 command to your device. As the message is retained in the MQTT broker, the device will receive it as soon as it connects to the MQTT broker.

    Example: Sending from another Tasmota

    In another Tasmota console you can type the following command:
    Publish2 cmnd/%topic%/DeepSleepTime 0
    To clear the retained message, once your device is out of DeepSleep, use:
    Publish2 cmnd/%topic%/DeepSleepTime

    Example: Sending using mosquitto utilities

    Command to send a retained message:
    mosquitto_pub -t "cmnd/myDeviceTopic/DeepsleepTime" -r -m "0"
    Remove retained message
    mosquitto_pub -t "cmnd/myDeviceTopic/DeepsleepTime" -r -n

    You can also send retained message or clear them with other MQTT tools: MQTT Explorer, ....

    Once you have made your configuration change, you will need to re-enable DeepSleep mode using DeepSleepTime command.

    Using smart home automation~

    Configure a settable flag in your home automation hub (e.g., Node-Red, openHAB, Home Assistant). The flag should subscribe to the tele/%topic%/LWT topic for the payload Online. Alternatively, if testing the payload value is not easy, subscribe to the topic tele/%topic%/STATE which is the 2nd topic on which the device publish right after waking-up.

    The moment a message is received on this topic, the automation can publish a message to topic cmnd/%topic%/DeepSleepTime with payload 0. This will cause the device to disable DeepSleep and allow maintenance such as firmware updates to be performed without having an unexpected DeepSleep event. Send the DeepSleepTime 0 command only once.

    Once device maintenance is completed, place it back into DeepSleep mode using original configuration.

    Rules~

    Example

    An example of a ruleset which deepsleeps a device with a RTC module attached during a certain portion of the day (i.e. at night). Mem1 is set to wakeup time in the morning (i.e. 540) and Mem2 to sleep time (i.e. 1080). For network attached devices with no RTC module, time#initialized trigger is better than system#init. Might be smart to have a DeepSleep gpio assigned if you need to access the device outside of normal awake hours.

    rule1 
    +on system#init do backlog event timecheck=%time%; ruletimer1 1 endon
    +on time#minute do event timecheck=%time%; ruletimer1 1 endon
    +on event#timecheck<%mem1% do Var1 3600 endon
    +on event#timecheck>%mem1% do Var1 0 endon
    +on event#timecheck>%mem2% do Var1 3600 endon
    +on rules#timer=1 do deepsleeptime %var1% endon
    +

    The following triggers can be used to execute commands upon wake-up or right before entering DeepSleep: - Power1#Boot : is the earliest trigger. But valid only if you have a Relay output defined. - Switch1#Boot : is the next trigger, also occur very early in the boot process. But valid only if you have Switch input defined. - System#Boot : is occurring later in the Tasmota boot process but is always available. - System#Save : is occurring right before a restart or before entering DeepSleep.

    For example the ruleset below turn on power right after Tasmota started, and turn it off just before entering DeepSleep

    Rule1 ON Power1#Boot DO Power on ENDON ON System#Save DO Power off ENDON
    +
    Any rule apply AFTER the Init() procedures of all sensors/drivers. This does mean that a POWER OFF on the GPIO may prevent sensors from being initialized correctly during start-up (e.g. DS18B20) and do not show up in teleperiod message. To ensure the GPIO is HIGH during restart you should define in the Configure --> Configure module --> GPIO x to Output Hi instead of RELAY 1 and avoid using any rules. To ensure the GPIO goes LOW after deepsleep you need to solder a 4.7K resistor between GND and the GPIO.

    Sequence is then as follow (only key lines are shown):

    00:00:00.085 CFG: Loaded from File, Count 122
    +00:00:00.095 Project tasmota demo-sensor Version 9.5.0(tasmota-sensors)
    +00:00:00.105 RUL: POWER1#BOOT performs "Power ON"
    +00:00:00.109 RSL: POWER = {"POWER":"ON"}
    +00:00:00.111 RSL: POWER = ON
    +00:00:04.454 WIF: Connecting to AP1 DEMOAP Channel 1 BSSId XX:XX:XX:XX:XX:XX in mode 11n as demo-sensor...
    +00:00:05.756 WIF: Connected
    +00:00:06.008 HTP: Web server active on dev-4119 with IP address 192.168.168.199
    +15:03:40.010 MQT: Attempting connection...
    +15:03:40.024 MQT: Connected
    +15:03:40.028 MQT: tele/demo-sensor/LWT = Online (retained)
    +15:03:40.032 MQT: cmnd/demo-sensor/POWER = 
    +15:03:40.047 MQT: stat/demo-sensor/POWER = {"POWER":"ON"}
    +15:03:40.050 MQT: stat/demo-sensor/POWER = ON (retained)
    +15:03:44.472 MQT: tele/demo-sensor/STATE = {"Time":"2021-06-27T15:03:44+02:00","Uptime":"0T00:00:13","UptimeSec":13,"Heap":28,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"MqttCount":1,"POWER":"ON","Wifi":{"AP":1,"SSId":"DEMOAP","BSSId":"XX:XX:XX:XX:XX:XX","Channel":1,"Mode":"11n","RSSI":86,"Signal":-57,"LinkCount":1,"Downtime":"0T00:00:07"}}
    +15:03:44.500 MQT: tele/demo-sensor/SENSOR = {"Time":"2021-06-27T15:03:44+02:00","Switch1":"OFF","Switch2":"ON"} (retained)
    +15:03:44.515 RUL: SYSTEM#SAVE performs "Power OFF"
    +15:03:44.524 MQT: stat/demo-sensor/POWER = {"POWER":"OFF"}
    +15:03:44.527 MQT: stat/demo-sensor/POWER = OFF (retained)
    +15:03:44.539 MQT: stat/demo-sensor/DEEPSLEEP = {"DeepSleep":{"Time":"2021-06-27T15:04:00","Epoch":1624799040}}
    +15:03:47.433 APP: Sleeping
    +

    Driver writer: Executing commands before entering DeepSleep~

    When writing a driver for a sensor, if the sensor supports a low power mode, it is a good practice to set the sensor in such low power mode in the FUNC_SAVE_BEFORE_RESTART handler. When Tasmota will restart at next wake-up, sensor will be automatically re-initialized.

    Example from xsns_09_bmp.ino:

    #ifdef USE_DEEPSLEEP
    +      case FUNC_SAVE_BEFORE_RESTART:
    +        BMP_EnterSleep();
    +        break;
    +#endif // USE_DEEPSLEEP 
    +

    In general you can also execute any command or special script BEFORE device goes into DeepSleep using handler FUNC_SAVE_BEFORE_RESTART as a predefined hook to implement your own procedure. This requires you to code your own function and self-compile custom firmware.

    Overcome network issues~

    If the device is not able to make a WIFI connection and get an IP during the first 15 seconds after boot it will go again without any further actions into deepsleep for another cycle. If you compile your own firmware you can change the timeout (e.g. 30 seconds) by setting #define DEEPSLEEP_NETWORK_TIMEOUT 30 in user_config.override or disable completely (device stay online until network connected) with #define DEEPSLEEP_NETWORK_TIMEOUT 0. If MQTT or NTP does not work the TELEPERIOD will execute anyhow and send the device to deepsleep afterwards. A wrong NTP will result in wrong timestamp send to MQTT. A missing MQTT connection will avoid any send.

    Another method to send the device into deepsleep after start is creating a rule like the one below. In this case the timeout can be configured online and changed.

    Rule1
    +  ON Dimmer#Boot DO RuleTimer1 30 ENDON
    +  ON Rules#Timer=1 DO DeepSleepTime 3600 ENDON
    +
    +Rule1 ON
    +

    DeepSleep Algorithm General Timing~

    Let's assume you have set DeepSleepTime 3600 (one hour) and TelePeriod 60 (one minute). The device will first wake at 8:00 am. The device will boot and connect Wi-Fi. Next, the correct time must be synchronized from one of the NTP servers and initial telemetry is sent.

    DeepSleep is then triggered after the next TelePeriod event. In this example, it will occur after one minute. Telemetry will be collected and sent (e.g., via MQTT) and DeepSleep can happen. First, Offline is published to the LWT topic on MQTT. It then calculates the new sleeping time to wake-up at 9:00 am (3600 seconds after the last wake-up). At 9:00 am this same sequence of events happens again.

    If you want to minimize the time that the device is in operation, two special values for TelePeriod exist: 10 seconds and 300 seconds. Using either of these two exact values will prevent waiting for the next telemetry. DeepSleep will be triggered within a few second of the time being synchronized rather than waiting for the TelePeriod.

    ESP8266 DeepSleep Side-effects~

    Not all GPIO behave the same during DeepSleep. Some GPIO go HIGH, some LOW, some FOLLOW the relay but work only on FET transistors. As soon as current flows they go LOW. I use one GPIO to trigger a BC337 transistor to switch OFF all connected devices during DeepSleep.

    Findings:

    Pin GPIO Behavior
    D0 16 Excluded due to use as wake-up pin
    D1 5 KEEP STATE, go LOW if resistance to ground < infinite
    D2 4 KEEP STATE, go LOW if resistance to ground < infinite
    D3 0 HIGH
    D4 2 HIGH
    D5 14 HIGH, go LOW if resistance to ground < infinite
    D6 12 HIGH, go LOW if resistance to ground < infinite
    D7 13 HIGH, go LOW if resistance to ground < infinite
    D8 15 LOW

    Log Output Explanation~

    (logging level 4)

    When MQTT connects at 13:08:38, this sets the system to READY.

    13:08:43 MQT: tele/tasmota/INFO3 = {"RestartReason":"Deep-Sleep Wake"}
    +13:08:44 APP: Boot Count 3
    +13:08:44 CFG: Saved to flash at F4, Count 96, Bytes 3824
    +

    In the context of DeepSleep, maintaining a device boot count is not relevant. When DeepSleep is enabled, boot count will not be incremented. This avoids excessive flash writes which will deteriorate the flash memory chip and eventually cause the device to fail. Boot count incrementing can be enabled using SetOption76.

    In this example, TelePeriod is 10. Therefore when it is reached, telemetry reporting occurs.

    13:08:48 MQT: tele/tasmota/STATE = {"Time":"2019-09-04T13:08:48","Epoch":1567595328,"Uptime":"0T00:00:14","UptimeSec":14,"Heap":24,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":20,"MqttCount":1,"Wifi":{"AP":1,"SSId":"MyWLAN","BSSId":"AA:FF:AA:AA:AA:AA","Channel":11,"RSSI":100,"LinkCount":1,"Downtime":"0T00:00:08","DeepSleep":300,"Heap":25160}}
    +13:08:48 MQT: tele/tasmota/SENSOR = {"Time":"2019-09-04T13:08:48","Epoch":1567595328,"ANALOG":{"A0":8}}
    +

    Date and time is set, status and telemetry sent. Now start shutdown procedure.

    First, send MQTT offline. 13:08:48 MQT: state/sonoff/LWT = Offline

    DeepSleep is 300 seconds. Therefore +-30 sec is allowed as deviation between proposed wake-up time and real wake-up time. Reporting in 0.1sec. In this case wake-up was one second late. 13:08:48 Timeslip 0.1 sec:? -300 < -10 < 300

    If the error is in the range, this is tagged as a normal wake up where drift can be recalculated 13:08:48 Normal deepsleep? 1

    Recalculate a new drift that is a multiplier for the next wake-up in 1/10000. In this case, the multiplier is 1.0257 13:08:48 % RTC new drift 10257

    And for information: New target wake-up time 13:08:48 Next wakeup 2019-09-04T13:10:00

    Based on run time and the error in the last loop, a new sleeping time will be calculated. This will be multiplied by the deepsleep_slip and, ideally, the device will wake up at the time above. 13:08:48 Sleeptime 285 sec, deepsleep_slip 10257

    The effectiveness of the compensation can be seen here. Instead of typically 160-200 seconds, most times it is better than 10 seconds in a one hour DeepSleep cycle.

    TempComp

    Rules to adapt DeepSleep period to battery level.~

    In some applications, it is interesting to adjust the DeepSleep period to the level of the battery. For this, we consider that:

    • the battery level is measured by the ESP ADC with the appropriate voltage divider,
    • the Tasmota ADC mode is set to Range mode,
    • AdcParams 6 has been used so the range value represent the battery voltage in millivolts
    • There is a BatteryPercentage feature that can be feeded by a rule and used here to drive DeepSleep behavior

    The below graph represent the adaptation paths we want to follow to adjust DeepSleepTime: one path while the level is dropping, another path while the level is rising.

    DeepSleep adjust graph

    To implement this in Tasmota we can use Rules. As Rules do not provide an AND operator, we also need the optional IF feature. This requires to compile a customized firmware with the following items in user_config_override.h:

    #define USE_RULES
    +#define SUPPORT_IF_STATEMENT
    +

    First, we need to know the current value of DeepSleepTime. Values for internal settings are not available as %variable% but they can generally be obtained by sending the command and capturing the result with a rule. To get the current value of DeepSleepTime, we need a trigger early in the boot process. For example Wifi#Connected.

    Our first rules to get the current DeepSleepTime into %var1% would be:

    ON Wifi#Connected DO Backlog DeepSleepTime ENDON
    +ON DeepSleepTime#Data DO var1 %value% ENDON
    +

    The best moment to compare the battery level and decide if it is necessary to change the DeepSleepTime is right after the TelePeriod SENSOR message has been sent, which is just before Tasmota calculate the next wake-up time. So we can use tele-ANALOG#Range as a trigger for our rules. Using BREAK allows to compare the trigger only to the upper value of the segment and stop further rules evaluations if the comparison is met. In the rule statement for a given battery level, we compare the current value of DeepSleepTime (which is %var1%) with the value we vant (given by the graph). If the current value is lower than the value we want, we are on the decreasing path and we can apply the new value. If the current value is greater than the value we want, we are on the rising path and we can apply the new value.

    DeepSleep adjust rules

    All above can be set into Rule1. As it is a long command, it may be necessary to enter the rule in 2 parts (note the usage of the +) :

    Rule1 on Wifi#Connected do backlog deepsleeptime endon on deepsleeptime#data do var1 %value% endon on tele-Analog#Range<3450 do if (%var1%<3600) deepsleeptime 3600 endif break on tele-Analog#Range<3650 do if (%var1%<1800) deepsleeptime 1800 endif break 
    +Rule1 +on tele-Analog#Range<3800 do if (%var1%<900) deepsleeptime 900 elseif (%var1%>1800) deepsleeptime 1800 endif break on tele-Analog#Range<4000 do if (%var1%>900)deepsleeptime 900 endif break on tele-Analog#Range>=4000 do if (%var1%> 600) deepsleeptime 600 endif endon
    +

    Don't forget to enable using Rule1 1

    DeepSleep Power Saving~

    The amount of power saved during deep sleep depends significantly on the type of ESP module used. For example, an ESP-M3 module drops to 20-25 uA at 2.5-3.3 V when in deep sleep. Other devices, with onboard regulators, USB chips etc. like the WEMOS D1 Mini, may still require 18 mA at 5 V when in deep sleep (i.e.: greater than 1000 times more power).

    \ No newline at end of file diff --git a/Device-Groups/index.html b/Device-Groups/index.html new file mode 100644 index 0000000000..9d00bbe544 --- /dev/null +++ b/Device-Groups/index.html @@ -0,0 +1 @@ + Device Groups - Tasmota
    Skip to content

    Device Groups~

    Allow devices to share values and control entire groups of devices

    A framework to allow multiple devices to be in a group with values such as power, light color, color temperature, brightness, PWM values, sensor values and more, all shared with other devices in the group. For example, with multiple lights in a device group, light settings can be changed on one light and the settings will automatically be changed on other lights. Dimmer switches could be in a device group with lights and that dimmer switch could control the power, brightness and colors of all the lights in the group. Multiple dimmer switches could be in a device group to form a 3-way/4-way dimmer switch.

    UDP multicasts, followed by UDP unicasts if necessary, are used to send updates to all devices so updates are fast. There is no need for an MQTT server but all the devices in a group must be on the same multicast network. The multicast address and port are specified at compile time with the DEVICE_GROUPS_ADDRESS (default = 239.255.250.250) and DEVICE_GROUPS_PORT (default = 4447) macros respectively.

    To enable device groups, execute command: SetOption85 1. All devices in a group must be running firmware with device group support and have device groups enabled.

    Operation~

    The device group name is set with the DevGroupName command (GroupTopic prior to v8.2.0.3). If a device group name is not set for a group, the MQTT group topic is used (with the device group number appended for device group numbers > 1). All devices in the same multicast network with the same device group name are in the same group. Some modules may define additional device groups. For example, if Remote Device Mode is enabled, the PWM Dimmer module defines three devices groups.

    The items sent and received from the group are selected with the DevGroupShare command. By default all items are sent and received. An example of when the DevGroupShare command would be used is when you have a group of lights that you control with a dimmer switch and home automation software. You want the dimmer switch to be able to control all items. The home automation software controls each light individually. When it controls the whole group, it actually sends command to each light in the group. If you use the home automation software to turn an individual light on or off or change it's brightness, color or scheme, you do not want the change to be replicated to the other lights. In this case, you would set the incoming and outgoing item masks to 0xffffffff (all items) on the dimmer switch (DevGroupShare 0xffffffff,0xffffffff) and set the incoming item mask to 0xffffffff and outgoing item mask to 0 on all the lights (DevGroupShare 0xffffffff,0).

    By default, the state of all relays, light settings, etc, are sent to and received from device group 1. To enable each relay (or light channels if option 68 is enabled) to be in a separate device group, enable option 88 (SetOption88 1). When option 88 is enabled, the DevGroupTie sets which relay each device group is tied to. Each device group can only be tied to a single relay. By default, relay 1 is tied to device group 1 (relay 1 updates are sent to/received from device group 1), device 2 is tied to device group 2, device 3 is tied to device group 3 and device 4 is tied to device group 4. If a device group is only to be used by DevGroupSend to send updates to a device group, tie it to device 0.

    To implement a device group with lights that change color in sequence (colors move progressively from light to light), use the SequenceOffset command to specify the offset of each light from the master. If your devices have friendly names or MQTT topics that end in the number of the light, SequenceOffset1 can automatically set the offset based on the friendly name/MQTT topic. Once the sequence offset is set, each light will delay light channel changes by the specified number of updates. For example, if you have three lights with friendly names Light1, Light2 and Light3, you can use the SequenceOffset1 command to set the sequence offsets. When you set the color on Light1 to red, then to green and then to blue, the color on Light2 will be green and the color on Light3 will be red. When you set the color on Light1 back to red, the color on Light2 will change to blue and the color on Light3 will change to green.

    When a device reboots or loses network connectivity, it requests the current status from device group members when it reconnects. There are times when devices in a device group may be set to a mix of power states, colors, brightnesses, etc. via automation software. For example, you may have a scene that sets the lights in the group to different colors. In this case, the devices in the group should not send light channel information until the color is changed on one of the dimmer switches/masters. To indicate that devices in a device group currently have one or more items that should not be kept in sync, publish/execute the DevGroupSend command with the item value prefixed with N on one of the devices that includes the item in its outgoing share mask. For example, the command DevGroupSend 224=NFF0000 executed on a dimmer switch in the group would set the color to red on the dimmer switch and inform the group that light channel information is not to be shared. You can then set the colors on the lights in the group to assorted colors and the dimmer switch will not change them. Once you set the color on the dimmer switch again (either with the Color command or with DevGroupSend without the N prefix), the syncing will resume and the colors on the lights will be changed to the that color. You can directly control which items are not shared until they are changed using the DevGroupSend command No Status Share (129) item.

    Troubleshooting~

    If no values seem to be shared between devices, perform the following checks:

    • Enter the commandSetOption85 on all devices in the group and make sure the result is ON on all devices.
    • Enter the command DevGroupName on all devices in the group and make sure the result for device group 1 is the exact same (case-sensitive) name on all devices.
    • Enter the command DevGroupShare -1,-1 on all devices in the group to enable sharing of all items.
    • Enter the command DevGroupSend 128=1 on one device in the group. If the power turns on on the other devices, the device groups feature is working.
    • Enter the command DevGroupStatus on all devices in the group. If you do not see all the other devices in the group listed as members, multicast packets are not being received by the devices. Check the network infrastructure that connects the devices together to make sure multicasts are enabled are not being filtered.

    Commands~

    Command Parameters
    DevGroupName<x> 0 = clear device group <x> name and restart
    <value> = set device group name and restart.\
    If a device group name is not set for a group, the MQTT group topic (GroupTopic) is used (with the device group number appended for device group numbers > 1).
    DevGroupSend<x> <item> = <value>[ ...] = send an update to device group <x>. The device group name must have been previously set with DevGroupName<x>. Multiple item/value pairs can be specified separated by a space. Spaces in <value> must be escaped with a backslash (\). The message sent is also processed on the local device as if it had been received from the network.

    For items with numeric values, <value> can be specified as @<operator>[<operand>] to send a value after performing an operation on the current value. <operator> can be + (add), - (subtract), ^ (invert), & (bitwise AND) or | (bitwise OR). If <operand> is not specified, it defaults to 0xffffffff for the invert operator and 1 for other operators.

    To indicate that an item should not be shared with the group until changed again, prefix the value with N.

    1 = Send the current value of all items. This will bring all the devices in sync after devices in a group have been set to different values (such as different colors on lights in a group). If N is not specified for the value, the list of items previously sent with an N prefix is cleared.
    3 = Light fade (0 = Off, 1 = On)
    4 = Light speed (1..40)
    5 = Light brightness (0..255)
    6 = Light Scheme
    7 = Light fixed color (0 = white (using CT channels), other values according to Color)
    8 = PWM dimmer low preset (0..255)
    9 = PWM dimmer high preset (0..255)
    10 = PWM dimmer power-on brightness (0..255)
    128 = Relay Power - bitmask with bits set for relays to be powered on. The number of relays can be specified in bits 24 - 31. If the number of relays is not specified, only relay 1 is set
    129 = No Status Share - DevGroupShare bitmask indicating which items should not be shared until changed.
    192 = Event - event name and arguments
    193 = Command - command and arguments
    224 = Light channels - comma separated list of brightness levels (0..255) for channels 1 - 5 (e.g. 255,128,0,0,0 will turn the red channel on at 100% and the green channel on at 50% on an RGB light) or hex color value (#RRGGBB, #RRGGBBWW, etc.)

    Examples:
    DevGroupSend 5=90 128=1 - send an update to set the light brightness to 90 and turn relay 1 on.
    DevGroupSend 193=Buzzer\ 2,3 - send the Buzzer 2,3 command.
    DevGroupSend 6=@+ 4=@-10 - set the next fixed color and decrease the brightness by 10.
    DevGroupSend 128=@^ - toggle all the relays.
    DevGroupSend 224=NFF0000 - set the color to red locally and inform the group that light channel information is not to be shared until changed.
    DevGroupSend 129=@\|18 - do not share light brightness or channel status until changed.
    DevGroupShare <in>,<out> = set incoming and outgoing shared items (default = 0xffffffff,0xffffffff)
    <in> and <out> are bit masks where each mask is the sum of the values for the categories (listed below) to be shared. For example, to receive only power (1), light brightness (2) and light color (16) and send only power (1), enter the command DevGroupShare 19,1.

    1 = Power
    2 = Light brightness
    4 = Light fade/speed
    8 = Light scheme
    16 = Light color
    32 = Dimmer settings (presets)
    64 = Event
    DevGroupStatus<x> Show the status of device group <x> including a list of the currently known members.
    DevGroupTie<x> <relay> = Tie the relay to the device group <x>. Only applies when option 88 is enabled.
    SequenceOffset<x> 0..255 = set device group color sequence offset. Color updates received from device group will be delayed by the specified number of updates.
    x = 0..2
    0 = set offset to <value>
    1 = set offset to friendly name 1 ending digits + <value> [default -1]
    2 = set offset to MQTT topic ending digits + <value> [default -1]
    Example.: For friendly name of Light4, SequenceOffset1 will set sequence offset to 3; SequenceOffset1 2 will set offset to 6.
    \ No newline at end of file diff --git a/Device-Recovery/index.html b/Device-Recovery/index.html new file mode 100644 index 0000000000..af613d0fcd --- /dev/null +++ b/Device-Recovery/index.html @@ -0,0 +1 @@ + Device Recovery - Tasmota
    Skip to content

    Device Recovery~

    Configuration problems can cause boot loops, erratic behavior, devices which will not appear (i.e., no tasmota-xxxx AP) or connect to Wi-Fi, etc. In cases such as these when there is no proper operation a recovery process is required.

    By default, the firmware tries to preserve the existing configuration (to support automated updates via OTA upgrades). However, various things can happen that cause the existing configuration to become problematic, e.g., when upgrading from old releases without following the migration path.

    When code updates change the values or the way settings are used, those code changes don't directly write the settings on the running device when you load the new firmware. What happens is that when it boots up, the firmware looks to see if it has a valid configuration (if it's an upgrade from an older Tasmota version) and if the CFG_HOLDER value is in the right place it assumes that the existing configuration is valid.

    If it doesn't find the right value it assumes that this is not a "simple" upgrade and takes the compiled-in configuration settings and writes them out to the configuration area.

    Recovery Techniques~

    Listed below are a few ways to reset the device to what is set in the firmware binary (my_user_config.h and user_config_override.h) aka firmware defaults, in order to recover a device:

    • Hold the button (Button1) down, if available, for 40 seconds. After that the device should reset and reboot. Fully cycle power after that is done to make sure everything is starting from scratch.
    • Issue Reset 1 command via the console, MQTT or HTTP. After the device reboots fully cycle power.
    • use Fast Power Cycle Device Recovery procedure

    Fast Power Cycle Device Recovery~

    Implemented for situations where a device cannot be reset to firmware defaults by other means (no serial access, no button). It resets ALL Tasmota settings (equal to Reset 1) after 7 power cycles.

    SetOption65 must be set to 0 (default) in order for this feature to be enabled.

    Warning

    If you have a weak power grid or frequent power brownouts it's best to disable this feature with SetOption65 1 immediately or you'll end up with firmware default devices after a brownout event.

    Procedure~

    1. Cut power from the device completely for 30 seconds
    2. Power the device on and off six times with intervals lower than 10 seconds and leave it on after seventh time
    3. Fast power cycle device recovery should activate and the device should be reset to firmware defaults

    If you flashed a precompiled binary you can reconfigure the device using the web UI after the reset.

    After Recovery~

    Once recovered, the device should be observed that it operates without instabilities before attempting to configure the device in any way. If the device still does not, follow these steps if you are able to configure its Wi-Fi and connect:

    1. Configure the device as Generic (18)
    2. Perform an OTA upgrade specifically to tasmota-minimal.bin. Taking this intermediate step ensures that the firmware will be reloaded. Since Tasmota performs a version comparison before performing the OTA update, explicitly changing the firmware that is on the device will ensure that the firmware is indeed replaced in the next step.
    3. Perform an OTA upgrade to the desired firmware variant.

    Once the device operates reliably, begin the configuration to set the GPIO configuration (via a Template or Module). Take any further configuration steps one at a time to ensure that after each setting is applied, the device continues to operate reliably.

    If the device exhibits defective behavior immediately after a particular individual setting is changed, then be aware that there may be a problem in the firmware. Please report this behavior via the Tasmota Discord Chat.

    That Didn't Work, What Now?~

    If none of these methods result in a working and reliable device, the only remaining option is to connect the device to the serial programming interface, erase the flash memory and flash a different precompiled firmware binary.

    \ No newline at end of file diff --git a/Displays/index.html b/Displays/index.html new file mode 100644 index 0000000000..4bbce55831 --- /dev/null +++ b/Displays/index.html @@ -0,0 +1,149 @@ + Displays - Tasmota
    Skip to content

    Displays

    This feature is included only in -displays.bin

    Supported Displays~

    DisplayModel Name Interface
    1 LCD display I2C
    2 SSD1306 OLED display I2C
    3 8x8 MATRIX display GPIO
    4 ILI934x TFT display
    DisplayIliMode 1 for ILI9341 or DisplayIliMode 3 for ILI9342
    When second SPI bus on ESP32 is used, SSPI must be defined instead of SPI
    SPI
    5 Waveshare E-Paper 2.9" display (black/white, partial update) SPI
    6 Waveshare E-Paper 4.2" display (black/white, full update) SPI
    7 SH1106 OLED display I2C
    8 ILI9488 TFT display SPI
    9 SSD1351 OLED color display SPI
    10 RA8876 TFT display SPI
    11 7 segment common anode display I2C
    12 ST7789 TFT display SPI
    14 SD1331 TFT display SPI
    15 TM1637, TM1638 and MAX7219 7-segment displays GPIO
    16 LilyGO T5 4.7" E-Paper display ESP32 device
    17 Universal Display Driver SPI or I2C
    18 Interface to virtual display driver with Berry
    19 MAX7219 Dot Matrix Interface GPIO
    20 TM1650 7-segment displays I2C

    Display Commands~

    See commands page for full list of available Display Commands

    DisplayMode~

    The display driver is able to display predefined setups of text or user defined text. To display text using DisplayText set DisplayMode to 0, or set DisplayMode to 1 for the HT16K33 dot-matrix display.

    To use the seven-segment-specific TM1637, TM1638 and MAX7219 or TM1650 Display- commands, set DisplayMode to 0.

    Parameter LCD Display OLED Display TFT Display 7-segment Display (TM163x, MAX7219 and TM1650)
    0 DisplayText DisplayText DisplayText All TM163x / TM1650 Display- functions
    1 Time/Date Time/Date Time/Date Time
    2 Local sensors Local sensors Local sensors Date
    3 MQTT and Time/Date Local sensors and Time/Date Local sensors and Time/Date Time/Date
    4 Local sensors MQTT and local sensors MQTT and local sensors NA
    5 MQTT and Time/Date MQTT, local sensors and Time/Date MQTT, local sensors and Time/Date NA

    DisplayText~

    The DisplayText command is used to display text as well as graphics and graphs on LCD, OLED and e-Paper displays (EPD). The command argument is a string that is printed on the display at the current position. The string can be prefixed by embedded control commands enclosed in brackets [].

    In order to use the DisplayText command the DisplayMode must be set to 0 (or optional 1 on LCD displays) or other modes must be disabled before compilation with #undef USE_DISPLAY_MODES1TO5.

    The DisplayText command is customised for the TM1637, TM1638 and MAX7219 or TM1650 seven-segment display modules. This is documented here and here.

    DisplayText Parameters~

    In the list below p stands for parameter and may be a number from 1 to n digits. On monochrome graphic displays things are drawn into a local frame buffer and sent to the display either via the d command or automatically at the end of the command.

    Positioning~

    lp = sets a character line to print at (on LCD display p = {0…}, on TFT display p = {1…})
    cp = sets a character column to print at (on LCD display p = {0…}, on TFT display p = {1…})
    xp = sets the x position for consecutive prints
    yp = sets the y position for consecutive prints

    Text is printed at the last provided position, either l or y for the vertical position, and either x or x for the horizontal position. Neither x nor y are advanced/updated after printing text.

    Line Primitives~

    hp = draws a horizontal line with length p (x is advanced)
    vp = draws a vertical line with length p (y is advanced)
    Lp:p = draws a line top:p (x,y are advanced)
    kp = draws a circle with radius p
    Kp = draws a filled circle with radius p
    rp:p = draws a rectangle with p with and p height
    Rp:p = draws a filled rectangle with p with and p height
    up:p:p = draws a rounded rectangle with p with, p height and p radius v Up:p:p = draws a filled rounded rectangle with p with, p height and p radius

    Miscellaneous~

    z = clear the display
    i = (re)init the display (in e-Paper mode with partial update)
    I = (re)init the display (in e-Paper mode with full update)
    d = update the display
    Dp = switch display drawing options:
    bit 0: auto updates => 1 auto draw on each displaytext cmd, 0 display must be updated manually with d
    ( only valid for bw oled and epaper displays, color displays draw always immediately)
    bit 1: character drawing => 0 opaque character drawing, 1 transparent character drawing
    o = switch display off
    O = switch display on
    ap = p (0..3) set rotation angle
    t = display Tasmota time in HH:MM
    tS = display Tasmota time in HH:MM:SS
    T = display Tasmota date in DD.MM.YY
    pp = pad text with spaces, positive values align left, negative values align right
    sp = set text scaling for all fonts (scaling factor 1...N)
    fp = set font (1=12, 2=24,(opt 3=8)) if font==0 the classic GFX font is used, if font==7 RA8876 internal font is used, if font==4 special 7 segment 24 pixel number font is used, a ram based font is selected if font==5
    Cp = set foreground color (0,1) for black or white and RGB decimal code for color (see color codes)
    Bp = set background color (0,1) for black or white and RGB decimal code for color (see color codes)
    Cip = set foreground index color (0..31) for color displays (see index color table below)
    Bip = set background index color (0..31) for color displays (see index color table below)
    wp = draws an analog watch with radius p (#define USE_AWATCH)
    Pfilename: = display an rgb 16-bit color (or jpg on ESP32) image when file system is present, Scripteditor contains a converter to convert jpg to special RGB16 pictures See ScriptEditor Ffilename: = load RAM font file when file system is present. the font is selected with font Nr. 5, these fonts are special binary versions of GFX fonts of any type. they end with .fnt. an initial collection is found in Folder BinFonts
    SXfilename: = load display descriptor for multiple display support (X = 1..3) for up to 3 displays. SX: = switch to display number (X = 1..3).
    dcI:V = define index color entry Index 19-31, V 16 bit color value (index 0-18 is fixed)

    Touch Buttons and Sliders~

    Requires #define USE_TOUCH_BUTTONS

    touch elements

    Draw up to 16 GFX buttons to switch real Tasmota devices such as relays or draw Sliders to dimm e.g. a lamp

    • Button number + 256 - a virtual touch toggle button is created (MQTT => TBT)
    • Button number + 512 - a virtual touch push button is created (MQTT => PBT)

    b#:xp:yp:xa:ys:oc:fc:tc:ts:text:
    Parameters are separated by colons.

    • b# where # = define a button number 0-15
    • xp = x position
    • yp = y position
    • xa = x size
    • ys = y size
    • oc = outline index color
    • fc = fill index color
    • tc = text index color
    • ts = text size on buttons
    • text: = button text (must end with a colon :) (max 9 chars)

    b0:260:260:100:50:2:11:4:2:Rel 1:

    Picture Buttons~

    To create picture touch buttons (jpg on ESP32 only) requires #define JPEG_PICTS and #define USE_UFILESYS.

    Upload pictures to the file system with a ".jpg" extension, then give the path to the picture as button text omitting the .jpg extension.

    Create a picture button with a picture file named wifi.jpg

    b0:260:260:100:50:2:11:4:2:/wifi:

    The size of the picture is not scaled and the dimensions of the button must fit the picture size. Clicked buttons will invert the colors of the picture.

    You may specify a picture for selected and unselected button state. Picture filename ending with '1' is used for unselected state and ending '2' is for selected state.

    Sliders~

    • bs# where # = is slider number 0..15
    • xp = x position
    • yp = y position
    • xa = x size
    • ys = y size
    • ne = number of elements
    • bc = background color
    • fc = frame color
    • bc = bar color

    Set the state of a button or slider with:

    • b#sX where # = is slider number 0..15
    • X = 0 or 1 for buttons, 0..100 for sliders

    Display JSON variables~

    Requires #define USE_DT_VARS

    Display variables that are exposed in JSON MQTT strings e.g. in TelePeriod messages.

    The values are updated every second.

    dv#:xp:yp:gc:fc:fo:ts:tl:dp:ut:JSON:ut:
    Parameters are separated by colons.

    • dv# where # = defines a variable number 0-7 (may be expanded by #define MAX_DT_VARS N)
    • xp = x position
    • yp = y position
    • gc = text background color (index color)
    • fc = text foreground color (index color)
    • fo = text font
    • ts = text size (negative value denotes transparent text)
    • tl = text field length (if negative align right)
    • dp = decimal precision (if < 0 denotes a string)
    • ut = update time in seconds (1...N)
    • jt = JSON VARIABLE NAME (uppercase) if you specify a string in brackets here it is treated as displaytext cmd
    • ut = unit string (max 5 chars and must end with a colon :)

    example:

    ;ILI9341 320x240 portrait mode
    +[x0y0P/corona.rgb:]
    +[dc19:31000]
    +[x60y30f2Ci3D2]Tasmota
    +; display text cmd displays time with seconds
    +[dv0:50:70:19:3:2:1:11:1:1:[tS]::]
    +; display text cmd displays analog watch
    +[dv1:120:250:19:2:2:1:11:1:5:[w40]::]
    +; displays Wifi SSID JSON
    +[dv2:10:10:0:3:1:-1:10:-1:1:WIFI#SSID::]
    +[x10y150f1s1Ci3Bi19]Counter:
    +; displays a sensor JSON variable (here counter1)
    +[dv3:80:150:0:7:1:1:11:0:1:COUNTER#C1:cnt:]
    +[x10y300f1s1Ci3Bi19]memory free:
    +; displays pre memory space JSON (heap)
    +[dv4:100:300:0:7:1:1:-7:-1:1:HEAP:kb:]
    +

    Line chart~

    (#define USE_GRAPH and #define NUM_GRAPHS 4 - maximum of 16)

    Up to 4 line charts may be defined.

    Ticks may be defined by adding tick numbers to the n parameter.

    Example

    n = graph number (0..3) + x ticks (16*number of x ticks) + y ticks (1024*number of y ticks).

    Gn:xp:yp:xs:ys:t:fmax:fmin defines a line chart:
    Parameters are separated by colons.

    • n = number up to 4 charts (0..3) + optional ticks
    • xp = x position
    • yp = y position
    • xs = x size (if xs<0) graph is not reinitialized on second call (e.g., restart of scripter)
    • ys = y size
    • t = time in minutes for total chart
    • ymin = float chart minimum y
    • ymax = float chart maximum y
    • icol = line color index (only for color graphs)

    gn:v adds a value to the chart buffer:

    • n = number up to 4 charts (0..3)
    • v = float value to add

    Gdn:m sets graph n draw mode 0 = off, 1 = on. When on, redraw graph

    • Gsn:path: = save graph n to path (if optional file system is present)
    • Grn:path: = restore graph n from path (if optional file system is present)

    Batch files~

    When a file system is present you may define displaytext batch files. If a file named "display.bat" is present in the file system this batch file is executed. The file may contain any number of diplaytext cmds, one at a line. You may have comment lines beginning with a ;

    Example

    ; clr screen
    +[z]
    +; draw full screen picture
    +[x0y0P/corona.rgb:]
    +; define index color
    +[dc19:31000]
    +; draw transparent text with new index color over picture
    +[x60y30f2Ci19D2]Tasmota
    +
    A displaytext batch file may be executed from console by displaybatch /file

    Color Codes~

    While computers and web design are generally using a 24-bit RGB888 color code built from a byte-triplet such as (255, 136, 56) or #FF8038, small color panels often use a more compact code 16-bit RGB565 color code. This means that the R, G and B coefficient are coded on less number of bits:

    • Red on 5 bits = 0..31
    • Green on 6 bits = 0..63
    • Blue on 5 bits = 0..31

    For Cp and Bp, p is calculated as p = 2048 * Red + 64 * Green + Blue

    Example

    Red 50% + Green 20% + Blue 100% = 2048 * 16 + 64 * 12 + 31 = 33576 equivalent to web #8033FF

    Common colors table:

    Color Code Color Code Color Code
    Black 0 Navy 15 Dark green 3
    Dark cyan 1007 Maroon 30720 Purple 30735
    Olive 31712 Light grey 50712 Dark grey 31727
    Blue 31 Green 7 Cyan 2047
    Red 63488 Magenta 63519 Yellow 65504
    White 65535 Orange 64800 Green yellow 45029
    Pink 64536

    Color Indices~

    Selected with Ci and Bi in the ILI9488, SSD1351, RA8876 and ST7789 color panels

    Index Color Index Color Index Color
    0 BLACK 1 WHITE 2 RED
    3 GREEN 4 BLUE 5 CYAN
    6 MAGENTA 7 YELLOW 8 NAVY
    9 DARKGREEN 10 DARKCYAN 11 MAROON
    12 PURPLE 13 OLIVE 14 LIGHTGREY
    15 DARKGREY 16 ORANGE 17 GREENYELLOW
    18 PINK

    You may expand the index color table up from index 19 to 31. the cmd [dcI:V] defines the index color with index I (19-31) to the 16 bit color value V

    Note on e-Paper Displays~

    E-Paper displays have 2 operating modes: full update and partial update. While full update delivers a clean and sharp picture, it has the disadvantage of taking several seconds for the screen update and shows severe flickering during update. Partial update is quite fast (300 ms) with no flickering but there is the possibility that erased content is still slightly visible. It is therefore useful to perform a full update in regular intervals (e.g., each hour) to fully refresh the display.

    Compilation directives: #define USE_SPI, #define USE_DISPLAY, #define USE_DISPLAY_EPAPER29, or #define USE_DISPLAY_EPAPER42

    Remark: the 4.2 e-Paper display requires about 15k of RAM. Therefore it only works with Core 2.42 and above.

    OLED Lifetime~

    The typical specifications for the lifetime of an OLED when permanently on is about 10000 hours (416 days). Dimming to 50% expands the lifetime to about 25000 hours.

    Burn-in~

    The data sheets of the TFT and OLED displays mention burn-in effects when a static display is shown for extended periods of time. You may want to consider turning on the display on demand only.

    Fonts~

    The EPD font contains 95 characters starting from code 32, while the classic GFX font contains 256 characters ranging from 0 to 255. Custom characters above 127 can be displayed. To display these characters, you must specify an escape sequence (standard octal escapes do not work). The ~character followed by a hex byte can define any character code.

    GFXFont:
    GFXFont

    EPDFont:
    EPDFont

    Hardware Connections~

    I2C displays are connected in the usual manner and defined via the GPIO component selection.

    The I2C address must be specified using DisplayAddress XX, e.g., 60. The model must be specified with DisplayModel, e.g., 2 for SSD1306. To permanently turn the display on set DisplayDimmer 100. Display rotation can be permanently set using DisplayRotate X (x = 0..3).

    On SPI the CS and DC pins when needed must use the pin definition with Display_ID + CS e.g. ST7789_CS

    E-Paper displays are connected via software 3-wire SPI (CS, SCLK, MOSI). DC should be connected to GND , Reset to 3.3 V and busy may be left unconnected. The jumper on the circuit board of the display must be set to 3-wire SPI.

    The ILI9488 is connected via hardware 3-wire SPI (SPI_MOSI=GPIO13, SPI_SCLK=GPIO14, CS=GPIO15) and must also be connected to the backlight pin The SSD1351 may be connected via hardware 3-wire SPI or 4-wire SPI with support for dimmer. The ILI9341 is connected via hardware 4-wire SPI, Backlight and OLEDRESET (dimmer supported on ESP32) Wiring

    The RA8876 is connected via standard hardware 4-wire SPI (SPI_MOSI=GPIO13, SPI_SCLK=GPIO14, RA_8876_CS=GPIO15, SSPI_MISO=GPIO12). No backlight pin is needed, dimmer supported, on ESP32 gpio pins may be freeley defined (below gpio 33).

    The ST7789 is connected via 4 Wire software SPI ((ST7789_CS), SSPI_SCLK, SSPI_MOSI, ST7789_DC, OLEDRESET, Backlight )

    Rule Examples~

    For scripting examples see Scripting Cookbook

    Print Text at size 1 on line 1, column 1:
    DisplayText [s1l1c1]Hello how are you?

    Draw a rectangle and draw text inside with size 2 and 7 chars padded with spaces:
    DisplayText [x85y95h130v30h-130v-30s2p7x90y100]37.25 C

    Refresh screen:
    DisplayText [z]

    Draw rectangle from x,y with width and height:
    DisplayText [x50y50r200:100]

    Display Local Sensors~

    (line breaks and indentation added to the rules for readability)

    Use Tasmota rules to display sensor values, time, and a separation line. Refresh the display every 60 minutes:

    rule1 on tele-SHT3X-0x44#Temperature do DisplayText [f1p7x0y5]%value% C endon
    +      on tele-SHT3X-0x44#Humidity do DisplayText [f1p10x70y5]%value% %[x0y20h296x250y5t] endon
    +      on tele-BMP280#Pressure do DisplayText [f1p10x140y5]%value% hPa endon
    +      on Time#Minute|60 do DisplayText [Tt] endon
    +

    Show 4 analog channels:

    rule1 on tele-ADS1115#A0 do DisplayText [s1p21c1l01]Analog1: %value% adc endon
    +      on tele-ADS1115#A1 do DisplayText [s1p21c1l3]Analog2: %value% adc endon
    +      on tele-ADS1115#A2 do DisplayText [s1p21c1l5]Analog3: %value% adc endon
    +      on tele-ADS1115#A3 do DisplayText [s1p21c1l7]Analog4: %value% adc endon
    +

    Show BME280 + SGP30:

    rule1 on tele-BME280#Temperature do DisplayText [s1p21x0y0]Temp: %value% C endon
    +      on tele-BME280#Humidity do DisplayText [s1p21x0y10]Hum : %value% %% endon
    +      on tele-BME280#Pressure do DisplayText [s1p21x0y20]Prss: %value% hPa endon
    +      on tele-SGP30#TVOC do DisplayText [s1p21x0y30]TVOC: %value% ppb endon
    +      on tele-SGP30#eCO2 do DisplayText [s1p21x0y40]eCO2: %value% ppm [s1p0x0y50]Time: [x35y50t] endon
    +

    WaveShare Display Drivers~

    Waveshare has two kinds of display controllers: with partial update and without partial update. The 2.9 inch driver is for partial update and should also support other Waveshare partial update models with modified WIDTH and HEIGHT parameters. The 4.2 inch driver is a full update display.

    epaper displays should be connected via software SPI. most of them require a reset and a busy line. connect the busy line to SSPI_MISO.

    The drivers are subclasses of the Adafruit GFX library. The class hierarchy is LOWLEVEL :: Paint :: Renderer :: GFX, where:

    • GFX: unmodified Adafruit library
    • Renderer: the interface for Tasmota
    • Paint: the modified pixel driver for e-paper
    • there are several virtual functions that can be subclassed down to LOWLEVEL

    The display dispatcher only does the class initialization call. All other calls go to the Renderer class.

    In black and white displays, a local RAM buffer must be allocated before calling the driver. This must be set to zero on character or TFT color displays.

    The EPD fonts use about 9k space, which can be selected at compile time using #ifdef directives.

    • SSD1306 - 1.15k
    • EPD42 - 2.57k
    • EPD29 - 2.1k
    • Display and Render class - ~12k

    Universal Display Driver~

    Universal Display Driver or uDisplay is a way to define your display settings using a simple text file and easily add it to Tasmota. uDisplay is DisplayModel 17. It supports I2C and hardware or software SPI (3 or 4 wire), 8,16 Bit parallel and RGB interface. The driver must be enabled by OPTION A3 on any GPIO pin.

    The driver is enabled by compiling with #define USE_UNIVERSAL_DISPLAY and setting an unused GPIO to Option A3.

    Descriptor File~

    The display itself is defined by a descriptor file. Many display descriptor files are included in Tasmota GitHub in tasmota/displaydesc folder

    which may be provided by any of the following methods:

    1. A display.ini file present in the flash file system. preferred option
    2. A special >d section in scripting. Copy the file to the >d script section and place a ->displayreinit cmd into >B section
    3. Copy the descriptor to Rule 3 but do not enable it (descriptor may not contain ANY spaces in this mode)
    4. Compile the descriptor into the binary in a section in user_config_override.h under driver 17 (const char)

    Options 2 and 4 work well for 1M flash devices.

    Descriptor text file has the following elements:

    :H

    Header line describes the main features of the display (comma separated, no spaces allowed)

    1. name
    2. x size in pixels
    3. y size in pixels
    4. bits per pixel (1 for bw displays, 16 for color displays)
    5. hardware interface used either I2C or SPI

    I2C

    I2C interface:

    1. I2C address in HEX
    2. SCL pin
    3. SDA pin
    4. RESET pin

    SPI

    SPI interface:

    1. Number (1 = hardware SPI 1, 2 = Hardware SPI 2 (ESP32), 3 = software SPI
    2. CS pin
    3. CLK pin
    4. MOSI pin
    5. DC pin
    6. Backlight pin
    7. RESET pin
    8. MISO pin
    9. SPI Speed in MHz

    PAR

    Parallel interface: (ESP32-S3 only)

    1. Bus size 8 or 16
    2. RESET pin
    3. CS pin
    4. DC pin
    5. WR pin
    6. RD pin
    7. Backlight pin
    8. d0-d7 pins
    9. d8-d15 pins if bus size = 16
    10. Parallel Speed in MHz (usually 20)

    RGB

    RGB 16 bit interface: (ESP32-S3 only)

    1. DE pin
    2. VSYNC pin
    3. HSYNC pin
    4. PCLK pin
    5. Backlight pin
    6. b0-b5 pins (blue color)
    7. g0-g5 pins (green color)
    8. r0-r4 pins (red color)
    9. Pixel clock Speed in MHz (usually 14)

    All signals must be given. Unused pins may be set to -1. If you specify a * char the pin number is derived from the Tasmota GPIO GUI.
    The CS and DC pins must be the standard pins e.g. SPI_CS or SPI_DC.

    Example

    :H,SH1106,128,64,1,I2C,3c,*,*,*
    +
    :H,ILI9341,240,320,16,SPI,1,-1,14,13,5,4,15,*,40
    +

    :S
    (optional) Splash setup, also defines initial colors. If omitted screen is not cleared initially.

    1. Font number, if -1 splash screen is suppressed
    2. Font size
    3. FG color (as index color)
    4. BG color (as index color)
    5. x position of text
    6. y position of text

    Example

    :S,2,1,1,0,40,20
    +

    :I
    Initial register setup for the display controller. (IC marks that the controller is using command mode even with command parameters) All values are in hex. On SPI the first value is the command, then the number of arguments and the the arguments itself. Bi7 7 on the number of arguments set indicate a wait of 150 ms. On I2C all hex values are sent to I2C.

    Example

    :I
    +EF,3,03,80,02
    +CF,3,00,C1,30
    +ED,4,64,03,12,81
    +E8,3,85,00,78
    +CB,5,39,2C,00,34,02
    +F7,1,20
    +EA,2,00,00
    +C0,1,23
    +C1,1,10
    +C5,2,3e,28
    +C7,1,86
    +36,1,48
    +37,1,00
    +3A,1,55
    +B1,2,00,18
    +B6,3,08,82,27
    +F2,1,00
    +26,1,01
    +E0,0F,0F,31,2B,0C,0E,08,4E,F1,37,07,10,03,0E,09,00
    +E1,0F,00,0E,14,03,11,07,31,C1,48,08,0F,0C,31,36,0F
    +11,80
    +29,80
    +

    :V video signal parameters for RGB panels
    hsync_polarity,
    hsync_front_porch,
    hsync_pulse_width,
    hsync_back_porch,
    vsync_polarity,
    vsync_front_porch,
    vsync_pulse_width,
    vsync_back_porch,
    pclk_active_neg,

    :o,OP
    OP = controller OPCODE to switch display off

    :O,OP
    OP = controller OPCODE to switch display on

    :R,OP,SL

    1. OP = rotation opcode
    2. SL = startline opcode (optional)

    :0
    :1
    :2
    :3

    Register values for all 4 rotations (color display only)

    1. rotation code
    2. x offset
    3. y offset
    4. rotation pseudo opcode for touch panel, in case of RGB panel use only these entries the appropriate coordinate convervsions are defined via pseudo opcodes 0 = no conversion 1 = swap and flip x 2 = flipx, flip y 3 = swap and flip y 4 = flip x 5 = flip y bit 7 = swap x,y

    :A
    3 OPCODES to set address window (all but epaper displays)

    1. set column opcode
    2. set row opcode
    3. start write opcode
    4. pixel size (optional)

    :P
    Pixel transfer size (default = 16 bit RGB) (optional)

    :i
    invert display opcodes
    1. inversion off
    2. inversion on

    :D
    dimmer opcode (optional)

    :B
    LVGL (optional)

    1. number of display lines flushed at once (min 10) the lower the lesser memory needed
    2. bit 0: DMA enables (0 for no DMA, 1 use DMA) - not supported on all displays
      bit 1: selects color swap, 2 = swap 16 bit color
      bit 2: enable async DMA, 0 wait for DMA to complete before returning, 4 run DMA async in the background. This later mode is only valid if the SPI bus is not shared between the display and any other SPI device like SD Card Reader,
      bit 3: 8 inverted busy line on epaper displays.

    :T
    Wait times used for E-paper display
    1. full refresh wait in ms
    2. partial refresh wait in ms
    3. wait after update in ms

    :f
    codes for epaper full refresh update

    :p
    codes for epaper partial refresh update

    beside the epaper chip codes, some pseudo opcodes are supported
    EP_RESET 60,1,T = toggle reset pin T milliseconds
    EP_LUT_FULL 61,0 = switch to full update mode
    EP_LUT_PARTIAL 62,0 = switch to partial update mode
    EP_WAITIDLE 63,1,T = wait for busy pin or T milliseconds
    EP_SET_MEM_AREA 64,0 = set memory area to full screen
    EP_SET_MEM_PTR 65,0 = set memory pointer to start of screen
    EP_SEND_DATA 66,0 = send framebuffer
    EP_CLR_FRAME 67,0 = send clr data
    EP_SEND_FRAME 68,0 = complete sendframedata sequence
    EP_BREAK_RR_EQU 69,X = break when reset reason == X
    EP_BREAK_RR_NEQ 6a,X = break when reset reason != X

    :L,size,OP
    Lookup table for full refresh (Waveshare 29)

    :l,size,OP
    Lookuptable for partial refresh (Waveshare 29)

    :Lx,size,OP
    Lookuptable for full refresh (Waveshare 42) x = 1..5
    size = number of bytes in table
    OP = opcode for sending refresh table

    :TIx,AA,SCL,SDA,<IRQ>,<RST>
    Defines a touch panel an I2C bus nr x (1 or 2)
    AA is device address
    SCL, SDA are the pins used (or * for tasmota definition)
    IRQ,RST optional IRQ and RST pins

    :TS,CS_PIN,(IRQ_PIN),(BUS Nr)
    Defines a touch panel an SPI bus with chip select CS_PIN (or *)
    optionally defines an IRQ_PIN (or -1) and the SPI BUS number

    :TR enable simple resistive touch via data lines (e.g. cheap il9341 displays)

    :M,X1,X2,Y1,Y2 Defines an optional mapping for touch controllers (always needed on resistive touch) X1 = display left margin
    X2 = display right margin
    Y1 = display upper margin
    Y1 = display lower margin

    :r,X Defines optional display rotation X = 0..3

    :b,X Defines optional inverted backpanel X = 1 = use inverted logic for backpanel

    Full configuration for SH1106 (comment lines starting with ; are allowed)

    :H,SH1106,128,64,1,I2C,3c,*,*,*
    +:S,0,2,1,0,30,20
    +:I
    +AE
    +D5,80
    +A8,3f
    +D3,00
    +40
    +8D,14
    +20,00
    +A1
    +C8
    +DA,12
    +81,CF
    +D9F1
    +DB,40
    +A4
    +A6
    +AF
    +:o,AE
    +:O,AF
    +:A,00,10,40
    +#
    +

    Full configuration for ILI9341: (comment lines starting with ; are allowed)

    :H,ILI9341,240,320,16,SPI,1,*,*,*,*,*,*,*,40
    +:S,2,1,1,0,40,20
    +:I
    +EF,3,03,80,02
    +CF,3,00,C1,30
    +ED,4,64,03,12,81
    +E8,3,85,00,78
    +CB,5,39,2C,00,34,02
    +F7,1,20
    +EA,2,00,00
    +C0,1,23
    +C1,1,10
    +C5,2,3e,28
    +C7,1,86
    +36,1,48
    +37,1,00
    +3A,1,55
    +B1,2,00,18
    +B6,3,08,82,27
    +F2,1,00
    +26,1,01
    +E0,0F,0F,31,2B,0C,0E,08,4E,F1,37,07,10,03,0E,09,00
    +E1,0F,00,0E,14,03,11,07,31,C1,48,08,0F,0C,31,36,0F
    +11,80
    +29,80
    +:o,28
    +:O,29
    +:A,2A,2B,2C
    +:R,36
    +:0,48,00,00,00
    +:1,28,00,00,01
    +:2,88,00,00,02
    +:3,E8,00,00,02
    +#
    +
    Scripter is the nost convenient way to edit and develop a uDisplay driver. On every scripter save the display is reinitialized and you immediately see results of your changes.

    Scripter driven display descriptor

    >D
    +>B
    +=>displayreinit
    +>d
    +; name,xs,ys,bpp,interface, address, scl,sda,reset
    +:H,SH1106,128,64,1,I2C,3c,*,*,*
    +:S,0,2,1,0,30,20
    +:I
    +AE
    +D5,80
    +A8,3f
    +D3,00
    +40
    +8D,14
    +20,00
    +A1
    +C8
    +DA,12
    +81,CF
    +D9F1
    +DB,40
    +A4
    +A6
    +AF
    +:o,AE
    +:O,AF
    +:A,00,10,40
    +#
    +

    Compiling~

    There are also many variants of each display available and not all variants may be supported.

    #define directive Description
    USE_DISPLAY Enable display support. Also requires at least one of the following compilation directives
    USE_DISPLAY_LCD Enable LCD display. Also requires USE_I2C
    USE_DISPLAY_SSD1306 Enable OLED SSD1306 display. Also requires USE_I2C
    USE_DISPLAY_MATRIX Enable MATRIX display
    USE_DISPLAY_ILI9341 Enable TFT ILI9341 display. Also requires USE_SPI
    if seconds SPI bus on ESP32 shall be used SSPI must be defined instead of SPI
    ILI9342 also supported, select with cmd displayilimode 3, default is: displayilimode 1 (ILI9341)
    USE_DISPLAY_EPAPER_29 Enable Waveshare EPAPER_29 display.(black/white, partial update)
    Also requires USE_SPI
    USE_DISPLAY_EPAPER_42 Enable Waveshare EPAPER_42 display.(black/white, full update)
    Also requires USE_SPI
    USE_DISPLAY_SH1106 Enable OLED SH1106 display. Also requires USE_I2C
    USE_DISPLAY_ILI9488 Enable TFT ILI9488 display. Also requires USE_SPI
    USE_DISPLAY_SSD1351 Enable color OLED SSD1351 display. Also requires USE_SPI
    USE_DISPLAY_RA8876 Enable TFT RA8876 display. Also requires USE_SPI
    USE_DISPLAY_SEVENSEG Enable 7 segment display. Also requires USE_I2C
    USE_DISPLAY_ST7789 Enable TFT ST7789 display. Also requires USE_SPI
    USE_DISPLAY_ILI9342 Enable TFT ILI9342 display. Also requires USE_SPI
    USE_DISPLAY_SD1331 Enable TFT SD1331 display. Also requires USE_SPI
    USE_DISPLAY_TM1637 Enable 7-segment TM1637, TM1638 and MAX7219 display.
    USE_DISPLAY_TM1650 Enable 7-segment TM1650 display. Also requires USE_I2C
    USE_DISPLAY_SEVENSEG_COMMON_ANODE Common anode 7 segment displays. Also requires USE_I2C
    USE_LILYGO47 Enable LILGO 4.7 Epaper display ESP32 combo
    USE_UNIVERSAL_DISPLAY Enable universal display driver
    USE_LVGL Enable LVGL, currently only supported by berry scripting
    USE_TOUCH_BUTTONS Enable virtual touch button support with touch displays
    SHOW_SPLASH Enable initialization splash message on the display
    USE_RAMFONT Enable loadable Fonts
    USE_MULTI_DISPLAY Enable multiple display support (up to 3)
    USE_AWATCH Enables analog watch support
    USE_GRAPH Enable line charts. Also requires NUM_GRAPHS

    \ No newline at end of file diff --git a/Domoticz/index.html b/Domoticz/index.html new file mode 100644 index 0000000000..ffcb8e9522 --- /dev/null +++ b/Domoticz/index.html @@ -0,0 +1 @@ + Domoticz - Tasmota
    Skip to content

    Domoticz

    Domoticz Tasmota supports Domoticz MQTT 'out of the box' for both relays and sensors.

    Find below the procedure to configure Domoticz and Tasmota.

    Prerequisites~

    The following servers should be made available:

    • You have installed/access to a MQTT broker server and made contact with your Tasmota device
    • You have installed Domoticz

    MQTT and Virtual Sensor~

    If not already done configure Domoticz MQTT and Virtual Sensor hardware.

    • On the hardware page add Type MQTT Client Gateway with LAN interface
      1. Give it a name
      2. Configure the interface with access to your MQTT server (Remote Address, Port, Username and Password)
      3. Set the Public Topic to flat (which seems to relate to out in the select field)
    • On the hardware page add Type Dummy (used for virtual switches)
      1. Give it a name

    Virtual Switch~

    Make a new virtual switch and remember its Idx number.

    1. Make a new virtual switch to be used with Sonoff by clicking Create Virtual Sensors
      1. Give it a name
      2. Select Sensor Type Switch
    2. On the Devices page find the new switch by its name
      1. Remember its Idx number

    Tasmota Configuration~

    Domoticz Tasmota provides different ways to configure Domoticz parameters. Choose the method you prefer:

    [The sections below don't look like they are needed any longer. The in topic and out topic entry areas don't appear to be in the Domoticz configure section - at least they are not there in mine and mine is working 17/03/2018]

    • Use the webinterface and select Configuration - Configure Domoticz:
      1. Set In topic to domoticz/in as hardcoded in Domoticz
      2. Set Out topic to domoticz/out as hardcoded in Domoticz
      3. Configure Idx 1 to the value read in step 2.i
    • Use MQTT and execute commands (if necessary, replace tasmota with unique topic you configured in Initital Configuration, see point 5 there):
      1. cmnd/tasmota/DzInTopic with payload domoticz/in as hardcoded in Domoticz
      2. cmnd/tasmota/DzOutTopic with payload domoticz/out as hardcoded in Domoticz
      3. cmnd/tasmota/DzIdx1 with payload value read in step 2.i
    • Use the serial interface and execute commands:
      1. DzInTopic with domoticz/in as hardcoded in Domoticz
      2. DzOutTopic with domoticz/out as hardcoded in Domoticz
      3. DzIdx1 with the value read in step 2.i

    Usage~

    That's it! You can now control your device from the Domoticz user interface.

    • On the Switches page scroll down and find your Switch as configured in step 1
      • Toggle the light bulb; Tasmota should respond

    Automatic Discovery~

    Tasmota supports automatic discovery by Domoticz through the Domoticz MQTT Discovery plugin.

    Prerequisites~

    The following services should be made available:

    • You have installed/access to a MQTT broker server and made contact with your sonoff
    • You have installed Domoticz
    • You have installed the Domoticz MQTT Discovery plugin

    MQTT Discovery Plugin~

    Configure Domoticz MQTT Discovery plugin.

    • On the hardware page add Type MQTT Discovery
      1. Give it a name, e.g. Tasmota
      2. Configure the interface with access to your MQTT server (MQTT Server Address, Port, Username and Password)
      3. Set the Discovery topic to homeassistant unless it has been changed in a custom Tasmota build
      4. Set the Ignored device topic to /tasmota/ to avoid unconfigured Tasmota devices from being discovered

    Tasmota Configuration~

    Precompiled Binary~

    • Each Tasmota device must have its own topic, the easiest way is to set topic to tasmota_%06X (%06X will be replaced by MAC address). See here for how to set the topic.
    • Use MQTT or Serial or Web console and execute commands (replace %topic% with the device's unique topic)
      1. cmnd/%topic%/SetOption19 with payload 1 to enable MQTT discovery

    Custom Binary~

    • The above settings can be defined in user_config_override.h (TBD)

    Usage~

    That's it! You will now find your Sonoff in the Domoticz user interface.

    • On the Switches page scroll down and find your Switch as configured in step 1
      • Toggle the light bulb; Sonoff should respond

    ...including sensors~

    \ No newline at end of file diff --git a/Download/index.html b/Download/index.html new file mode 100644 index 0000000000..527deecbac --- /dev/null +++ b/Download/index.html @@ -0,0 +1 @@ + Download - Tasmota
    Skip to content
    \ No newline at end of file diff --git a/Dynamic-Sleep/index.html b/Dynamic-Sleep/index.html new file mode 100644 index 0000000000..8358609bd7 --- /dev/null +++ b/Dynamic-Sleep/index.html @@ -0,0 +1,2 @@ + Dynamic Sleep - Tasmota
    Skip to content

    Dynamic Sleep

    Dynamic Sleep (CPU Main loop target / CPU Power Management)~

    SetOption60 may be used to configure your device to use Normal Sleep or Dynamic Sleep.

    Dynamic Sleep is enabled by default from Tasmota version 6.3.0.15 but may be reconfigured by setting the value of SetOption60 accordingly.

    Command Description
    SetOption60 0 Dynamic Sleep is ENABLED (Default since 6.3.0.15)
    SetOption60 1 Normal Sleep is ENABLED

    The term CPU is used loosely here for the sake of making it easier to understand - When the term CPU is used it is actually referring to the ESP8266 SoC Micro Controller.

    With the introduction of many new drivers, sensors and other functions as part of the Tasmota firmware, it has become more important to pay specific attention to the amount of microcontroller clock cycles shared with the underlying SDK/Arduino ESP8266 Core.

    The main application loop of the Tasmota firmware needs to visit each of the driver callbacks within the main loop to make sure all the required drivers and sensors receive the necessary processing time whilst ensuring that the main loop does not overwhelm the need for processing time by the SDK / Arduino ESP8266 core.

    The highest priority drivers/sensors need to be called once per 50ms to operate as designed but most of the normal run of the mill drivers and sensors do not necessarily require this amount of intense polling. The 50-millisecond mark would normally be considered to be an absolute minimum duty cycle for the main processing loop on ESP8266 boards whilst most Sonoff device derivatives will function perfectly well way above this default setting.

    To make this manageable from device to device a new setting has been introduced enabling the setting of the main loop target to a specific value in milliseconds.

    For default operation, this will be set to 50 milliseconds as there are generally no drivers or sensors that need to be polled at a rate higher than this.

    To allow for power usage flexibility this value may also be increased to a value of up to 250 milliseconds which is very useful to reduce power and processing demand on non-time critical devices such as switches (which is what most of Tasmota is used for.)

    The purpose of this setting is to allow you as a user to set the speed at which driver and sensors will be serviced and as a result also the amount of time given to the SDK / Arduino ESP8266 core to handle its background tasks (which are not under direct control of the Tasmota firmware.)

    Example Use Case~

    Let's assume the default value of 50 for sleep and that a simple device such as a Sonoff Basic R1 or R2 is being used.

    In this case, the main firmware loop will iterate through all the drivers and sensors once per 50 milliseconds.

    Current tests suggest that a simple device such as a Sonoff Basic requires only about 9.5 milliseconds to complete one iteration of servicing all the drivers and sensors enabled in the standard tasmota.bin firmware.

    The time management functionality offered by dynamic sleep will compute this time requirement automatically and allow the SDK / Arduino ESP8266 Core to service background tasks such as maintaining WiFi connectivity for the remainder of the time not spent in the main firmware loop - i.e. in the case of sleep 50 this would mean 50 milliseconds - ~9.5 milliseconds = ~40.5 milliseconds spent outside of the main firmware loop servicing SDK / ESP8266 Core functions which automatically consume fewer clock cycles when there is nothing intense for the SDK / ESP8266 Core to maintain or perform.

    Normal Sleep was previously the only option for Tasmota powered devices wishing to take advantage of power saving but it does have the disadvantage that the sleep would be a constant setting insofar that the entire firmware codebase would run at a pre-determined speed causing some drivers to run slower than expected and decreasing the speed at which services such as the WebUI is rendered (This varies between the various underlying cores depending on which version is used.)

    Using Dynamic Sleep (SetOption60 = 0) instead of Normal Sleep (SetOption60 = 1) has the advantage that CPU time will be given to any particular driver or process (let's say the WebUI) on demand as and when needed whilst spending most of its time waiting for the next main loop iteration to occur.

    During this time of waiting the ESP8266's power demand can go from 80mA all the way down to 20mA which yields great benefits for power saving vs. firmware responsiveness compared to the traditional sleep setting.

    Normally the target main loop setting would be 50 milliseconds. The firmware will service all the driver and sensor callbacks up to a maximum of 20 times per second. In most cases, this is unnecessary as most normal sensors like temperature sensors only need polling once per second. So, whether you poll the temperature sensor 20 times per second (sleep 50) or 5 times per second (sleep 200) it has no impact on the functionality.

    Allowing the main loop to iterate 20 times per second vs. 5 times per second is obviously more time consuming and processor consuming leaving less time to idle (i.e., save power).

    For example, if you were using an MCP230xx with interrupts, and you need a high interrupt response then sleep 50 or lower would make sense since that specific driver can poll once per 50 milliseconds to check for interrupts. Most other sensor polling can be done in intervals longer than 50 milliseconds so you achieve more idle time, and therefore also more sleep time so it saves power.

    Monitoring Performance~

    Given all the above it is an obvious conclusion that in order to manage something you would need to be able to measure it. For this reason two new variables have been added to the telemetry data namely LoopSet and LoadAvg and are represented in the telemetry JSON as follows:

    MQT: tele/sound1/STATE = {"Time":"2018-11-26T17:41:27","Uptime":"0T05:05:17","Vcc":3.504,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"POWER":"OFF","Wifi":{"AP":1,"SSId":"Wireless","BSSId":"DE:AD:00:00:BE:EF","Channel":3,"RSSI":100}}
    +

    The two values indicated for LoopSet and LoadAvg have the following relation:

    Variable Value Description
    SleepMode Normal Normal Sleep mode is enabled (SetOption60 = 1)
    SleepMode Dynamic Dynamic Sleep mode is enabled (SetOption60 = 0)
    Sleep 50 Current setting for sleep
    LoadAvg 19 Reported % time of Sleep spent doing Tasmota main loop processing

    In this example, 19% of 50 milliseconds would be 9.5 milliseconds (19/100*50), so we can see that there is sufficient headroom for the SDK / ESP8266 Arduino Core to do its background work.

    On some devices which have many sensors connected you may observe the LoadAvg value exceeding 100 - This means that you have not set the value of sleep high enough to accommodate all the sensors and drivers which need to be serviced.

    In the latter case, you have two options - either increase the value of sleep to a higher one to maintain a load average well below 100 or use multiple devices to spread the load across separate Tasmota powered devices/boards.

    For the most part, all Sonoff based products should perform well balanced with the default setting of 50 for sleep.

    How to use Dynamic Sleep~

    From serial console, or webui console enter command

    SetOption60 0 and Sleep xx

    Where xx is the number of milliseconds you wish to target your main processing loop at ranging from 0 through to 250.

    Should you set a sleep value that is too low you will observe output on telemetry for the value of LoadAvg to be in excess of 100 - This is not ideal and should be avoided as it starves the Arduino Core / SDK of the needed processing time to take care of background tasks such as WiFi management.

    For optimal operation of the Tasmota firmware, it is recommended to keep your device running at a LoadAvg value of 75 or lower. If your device does not have any time critical drivers/sensors connected you are encouraged to increase the sleep value to a higher value to gain from the power saving benefits thereof.

    \ No newline at end of file diff --git a/EQ3-TRV/index.html b/EQ3-TRV/index.html new file mode 100644 index 0000000000..8dc6f9eebd --- /dev/null +++ b/EQ3-TRV/index.html @@ -0,0 +1,57 @@ + EQ3 TRV - Tasmota
    Skip to content

    EQ3 TRV

    This driver allows the control of Eqiva TRV's (i.e. Thermostat Radiator Valve). Compatible models are:

    • Eqiva eQ-3 Bluetooth Smart (141771E0/141771E0A)
    • Eqiva eQ-3 Bluetooth Smart(UK Version) (142461D0)

    Other Eqiva EQ3 models should work as well, but make sure you select a Bluetooth model as there are also non-Bluetooth models.

    Compiling from source~

    In order to have EQ3 valves working when compiling from source you need to add these define in user_config_override.h:

    #ifdef USE_EQ3_ESP32
    +   #undef USE_EQ3_ESP32
    +#endif
    +#define USE_EQ3_ESP32
    +

    Setup~

    Before you can use the TRV you will need to enable Bluetooth on the TRV:

    1. Press the Mode/Menu button for at least 3 seconds.
    2. Select the menu item bLE with the control wheel and confirm by pressing the control wheel shortly.
    3. The display will show OFF to deactivate the function or On to activate the function.
    4. Confirm by pressing the control wheel shortly.

    Note: No need to pair the TRV

    Next you will need to make sure that BLE is enabled in Tasmota:

    1. Configuration
    2. Configure BLE
    3. Enable Bluetooth

    To determine the mac addresses of a TRV:

    1. Go to the BLE menu in Tasmota
    2. Enable active scan
    3. In to the tasmota console: TRV devlist

    This will give you the mac address of each valve.

    Note

    • Enable 1 valve at a time as this makes it easier to identify
    • You might need to wait a minute or so or repeat the "TRV devlist" command a few times before the devices have been properly identified
    • Keep in mind that the TRV does NOT report the current temperature, only the requested, target, temperature. The Xiaomi Thermometer LYWSD03MMC makes a perfect combo for measuring the room temperature (~USD 4)

    After configuring, tasmota will poll the discovered valves and publish their state under stat/EQ3/<MAC Adress> (or stat/EQ3/<BLEAlias> if you have configured an alias for the MAC address of the valve using BLEAlias).

    The interval between polls can be configured using TRVPeriod. Tasmots needs to have an NTP or RTC time configured for this to work.

    Operating your TRV~

    There are 2 ways to control your TRV:

    • The Tasmota Console (convenient for setup)
      syntax: TRV <MAC Address> <command> [options]
      example: TRV 001A2216A458 settemp 21.5
    • MQTT:
      syntax: cmnd/<tasmota_topic>/EQ3/<MAC Address>/command [options]
      example: cmnd/ble_esp32/EQ3/001A2216A458/settemp 22.5

    As you can see from the example the MQTT topic is made of:

    • Standard Tasmota %prefix% : cmnd, stat
    • %topic% of the BLE_ESP32 gateway device, here ble_esp32
    • An EQ3 element to specify this command is specific to the EQ3 driver
    • The MAC address or alias of the EQ3
    • The command to the EQ3 or result from the EQ3

    The EQ3 TRV has 3 modes of operation:

    Mode Description
    auto follows the week program. A temperature different from the week program can be set any time, but at the next programmed timeslot the TRV will switch back to the given temperature
    manual keeps the current requested temperature
    holiday keeps the temperature requested for the period of holiday and then switches back to the mode that was active before: auto or manual

    These 3 modes can be set and configured using different commands described below. After submitting a command you will see one or more of the possible results.

    Status Description
    queued Command has been accepted by the BLE driver
    DONENOTIFIED Command has been successfully processed by the TRV and the results are send in a json format
    ignoredbusy Currently we can only accept a single command in the queue, during the processing of a TRV command subsequent commands will be rejected. Please resubmit.
    FAILCONNECT After 3 automatic retries we were not able to contact the TRV and we give up. Please resubmit

    Under normal circumstance you will get a JSON formatted response from the valve:

    {
    +  "cmd": "settemp",
    +  "result": "ok",
    +  "MAC": "001A2216A458",
    +  "tas": "ble-esp32-0936",
    +  "RSSI": -79,
    +  "stattime": 1642328707,
    +  "temp": 21.0,
    +  "posn": 95,
    +  "mode": "auto",
    +  "hassmode": "auto",
    +  "boost": "inactive",
    +  "dst": "set",
    +  "window": "closed",
    +  "state": "unlocked",
    +  "battery": "GOOD",
    +  "holidayend": "00-00-00 00:00",
    +  "windowtemp": 12.0,
    +  "windowdur": 15,
    +  "day": 21.0,
    +  "night": 17.0,
    +  "offset": 0.0
    +}
    +

    If the mode is holiday, "holidayend" will show when the holiday period and mode is about to end:

    {
    +  "mode": "holiday",
    +  "holidayend": "22-01-19 17:00"
    +}
    +

    In the response for the command setprofile, "profiledayset" will be added:

    {
    +  "cmd": "setprofile",
    +  "profiledayset": 4
    +}
    +

    In the response for the command reqprofile, "profiledayn" (n=0…6) will be added:

    {
    +  "cmd": "reqprofile",
    +  "profileday4": "17.0-07:00,23.0-10:00,17.0-17:00,21.0-23:00,17.0-24:00"
    +}
    +
    Field Description
    cmd recent command the response is given for
    MAC mac address. It is always the mac address even if an alias was used in the command.
    tas hostname of your tasmota
    RSSI BLE signal strength
    stattime seconds since Unix Epoch (January 1st, 1970)
    temp target temperature
    posn valve position (0=closed / 100=fully opened)
    mode manual / auto / holiday
    hassmode mode for Home Assistant usage: auto (=mode auto) / off (valve is set to frost protection = off) / heat (valve is open) / idle (valve is closed)
    boost boost mode (valve opened 80 % for 5 minutes): active / inactive
    dst daylight savings time: set / unset
    window status of the window open functionality (activated when the temperature suddenly drops): open / closed
    state child lock enabled (disables the buttons on the TRV): locked / unlocked
    battery battery status of the TRV: GOOD / LOW
    holidayend end of holiday mode
    windowtemp window open temperature
    windowdur window open duration
    day comfort temperature
    night reduction temperature
    offset offset temperature
    profiledayset day the profile was set for: (0 … 9). Only included for command setprofile
    profiledayn profile for the day it just has been requested for. (n=0…6). Only included for command reqprofile

    Available commands~

    Base commands~

    Command Description and parameters
    trvperiod Display/Set the EQ3 poll interval in seconds. In this interval to every TRV matching the following criteria a poll (=state) command will be sent automatically.
    trvonlyaliased Display/Set the EQ3 OnlyAliased parameter
    set to 1 for any aliased BLE devices
    set to 2 for only aliases starting with EQ3
    trvMatchPrefix Display/Set the EQ3 MatchPrefix parameter
    set to 1 to not require active scan to identify EQ3 - identify from MAC (default)
    Set to 0 to disable this matching

    TRV subcommands~

    Subcommand Description and parameters
    devlist Display all TRV's which have been found in BLE scan mode.
    No parameters.
    scan Alias of devlist.
    No parameters.
    state Current valve state without changing anything. (except the time on the valve)
    No parameters.
    Note: If your ESP32 tasmota is not synchronized with a valid date and time, this command will set the wrong time and date to the TRV. See settime.
    settemp Set the desired target temperature.
    temperature.
    valve Control the valve state.
    off Enable frost protection
    on Open the valve completely
    Note: If the current mode is auto or holiday: at the next programmed timeslot the valve will switch back to the given temperature. To set the valve permanently please use direct commands on or off.
    on Set mode to manual and enable frost protection.
    Note:Temperature will be reported as 30.0 C.
    off Set mode to manual and open the valve completely (saves potentially battery in summer while the central heating is not working).
    Note:Temperature will be reported as 4.5 C.
    mode Define the current operating mode.
    auto same as auto, see below
    manual same as manual, see below.
    on same as on, see above.
    off same as off, see above.
    heat same as on, see above.
    cool same as off, see above.
    Note: The 3rd mode holiday can only be set with the setholiday command
    auto Define auto as the current operating mode. Run the week program as stored in the TRV.
    Note: When setting a temperature, switch to day or night temperature: at the next programmed timeslot the TRV will switch back to the given temperature.
    manual Define manual as the current operating mode. Disable the week program and keep the temperature as selected with settemp / day / night
    day Set to comfort temperature
    night Set to reduction temperature
    setdaynight Change the comfort and reduction temperature.
    daytemp nighttemp.
    boost Activate boost mode (valve 80% open for 5 minutes).
    Note: boost mode will stop automatically after 5 minutes.
    unboost Deactivate boost mode
    lock Disable TRV buttons
    unlock Enable TRV buttons
    settime Synchronize current tasmota time to the TRV:
    No parameters.
    Send an alternate time to the TRV:
    yyMMddhhmmss
    (byte by byte conversion from decimal to hexadecimal).
    Note: If your ESP32 tasmota is not synchronized with a valid date and time, this command (with no parameters) will set the wrong time and date to the TRV.
    setprofile Set the temperature schedule for the given day. (0=Saturday, 1=Sunday, … 6=Friday)
    Up to seven pairs of temperature-timeslot (e.g. 20.5-07:30) can be given.
    day temperature-timeslot, temperature-timeslot
    It is also possible to set a couple of days with one command: use 7=weekend, 8=workday, 9=everyday for this purpose.
    Note: The last timeslot shall always be -24:00, otherwise a default temperature is used for this timeslot.
    reqprofile Read the temperature schedule for the given day. **(0=Saturday, 1=Sunday, … 6=Friday)
    day**
    setholiday Define holiday as the current operating mode.
    end-date,end-time temperature.
    holiday mode will automatically terminate and resume the former mode and temperature when the end date and time are reached.
    Note: During this period it makes no sense to apply any commands for setting different temperatures. For manually ending holiday mode call auto or manual. Only then other commands will be accepted again.
    setwindowtempdur set window open temperature and duration in minutes.
    temperature minutes.
    offset set offset temperature
    temperature

    Examples~

    Request the current status without changing anything:

    cmnd/tasmota/EQ3/001A2216A458/state
    +

    set a target temperature (21.5 C)

    cmnd/tasmota/EQ3/001A2216A458/settemp 21.5
    +

    Select TRV mode auto: run the week program as stored in the TRV

    cmnd/tasmota/EQ3/001A2216A458/auto
    +

    Select TRV mode manual: disable the week program and keep the temperature as selected (settemp/day/night)

    cmnd/tasmota/EQ3/001A2216A458/manual
    +

    Select TRV mode holiday: suspend the week program or manually applied temperature until 2022 - Jan - 19 - 17:00 and set the temperature to 18.5 C

    cmnd/tasmota/EQ3/001A2216A458/setholiday 22-01-19,17:00 18.5
    +

    Select TRV temperature day: Switch to comfort temperature

    cmnd/tasmota/EQ3/001A2216A458/day
    +

    Select TRV temperature night: Switch to reduction temperature

    cmnd/tasmota/EQ3/001A2216A458/night
    +

    Note

    If auto is the current mode: When setting a temperature, switch to day or night temperature, the TRV will switch back to the temperature according the next programmed timeslot.

    Disable the TRV and enable frost protection.

    Until the next programmed timeslot

    cmnd/tasmota/EQ3/001A2216A458/valve off
    +

    Permanently:

    cmnd/tasmota/EQ3/001A2216A458/off
    +

    Disable the TRV and open the valve completely (saves potentially battery in summer while the central heating is not working):

    Until the next programmed timeslot

    cmnd/tasmota/EQ3/001A2216A458/valve on
    +

    Permanently:

    cmnd/tasmota/EQ3/001A2216A458/on
    +

    Change the comfort and reduction temperature to 22 C and 17.5 C

    cmnd/tasmota/EQ3/001A2216A458/setdaynight 22 17.5
    +

    Enable boost mode (valve 80% open for 5 minutes)

    cmnd/tasmota/EQ3/001A2216A458/boost
    +

    Disable TRV buttons

    cmnd/tasmota/EQ3/001A2216A458/lock
    +

    Synchronize current tasmota time with the TRV

    cmnd/tasmota/EQ3/001A2216A458/settime
    +

    Set the time and date (byte by byte conversion from decimal to hexadecimal)

    • Date: 2021 - jan - 04 - 13:00:00
    • In hex: 15 - 01 - 04 - 0d:00:00 (yyMMddhhmmss)
    • Concatenate: 1501040d0000
    cmnd/tasmota/EQ3/001A2216A458/settime 1501040d0000
    +

    Set the temperature schedule for day 3 (Tuesday) as

    • 20.5 C until 07:30
    • 17.0 C until 17:00
    • 22.5 C until 22:00
    • 18.0 C until 24:00
    cmnd/tasmota/EQ3/001A2216A458/setprofile 3 20.5-07:30,17.0-17:00,22.5-22:00,18.0-24:00
    +
    \ No newline at end of file diff --git a/ESP32-Devices/index.html b/ESP32-Devices/index.html new file mode 100644 index 0000000000..9541e4b9f0 --- /dev/null +++ b/ESP32-Devices/index.html @@ -0,0 +1,56 @@ + ESP32 Devices - Tasmota

    ESP32 Devices

    LilyGO TTGO T-Camera OV2640_V05~

    In platformio_override.ini uncomment the line with tasmota32 and set the correct COM port.

    In user_config_override.h add:

    #define USE_BMP
    +#define USE_I2C
    +#define USE_SPI
    +#define USE_DISPLAY
    +#define USE_DISPLAY_SSD1306
    +#define SHOW_SPLASH
    +#define USE_WEBCAM
    +

    Upload via USB, then apply the following Template:

    {"NAME":"TTGO_V05","GPIO":[1,1,1,1,5090,5088,1,1,5056,5024,5089,5091,1,1,5092,5184,0,640,608,5093,0,5152,4928,5120,0,0,0,0,4992,160,32,1,5094,0,0,5095],"FLAG":0,"BASE":2}
    +

    The PIR will turn ON/OFF the display and send over MQTT the POWER status. The display shows the sensor data. To make the device work nicely, change the following settings in the Console:

    Camera settings to correct orientation (USB on the bottom):

    WCFlip ON
    +WCMirror ON
    +WCResolution 6
    +WCSaturation 0
    +WCBrightness -1
    +WCContrast 1
    +

    Display Settings (USB on the bottom):

    DisplayRotate 2
    +DisplayCols 21
    +DisplayRows 7
    +DisplayMode 2
    +

    BME280 Settings (it is not very trustable):

    HumOffset 10
    +TempOffset -15
    +

    PIR/Button Settings:

    SwitchMode1 1
    +SetOption73 1
    +

    ODROID-GO~

    is supported via build Tasmota32-lvgl. Upload firmware and configure via Menu Auto-configuration

    To make the device work nicely, change the following settings in the Console:

    adcparam3 6,0,4095,0,6160
    +

    Rule for Joystick to dim the display:

    on analog#joy2=1 do dimmer - endon on analog#joy2=2 do dimmer + endon
    +

    AITHINKER CAM~

    {"NAME":"AITHINKER CAM","GPIO":[4992,1,672,1,416,5088,1,1,1,6720,736,704,1,1,5089,5090,0,5091,5184,5152,0,5120,5024,5056,0,0,0,0,4928,576,5094,5095,5092,0,0,5093],"FLAG":0,"BASE":2}
    +

    wESP32~

    {"NAME":"wESP32","GPIO":[0,0,1,0,1,1,0,0,1,1,1,1,5568,5600,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1],"FLAG":0,"BASE":1}
    +

    WT32-ETH01~

    {"NAME":"WT32-ETH01","GPIO":[1,1,1,1,1,1,0,0,1,0,1,1,3840,576,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,1],"FLAG":0,"BASE":1}
    +

    Denky (Teleinfo)~

    {"NAME":"Denky (Teleinfo)","GPIO":[1,1,1,1,5664,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1376,1,1,0,0,0,0,1,5632,1,1,1,0,0,1],"FLAG":0,"BASE":1}
    +

    Olimex ESP32-PoE~

    {"NAME":"Olimex ESP32-PoE","GPIO":[1,1,1,1,1,1,0,0,5536,1,1,1,1,0,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1],"FLAG":0,"BASE":1}
    +

    M5Stack Atom~

    {"NAME":"M5Stack Atom","GPIO":[1,1,1,1,1,1,1,1,1056,1,1,1,1,1,1,1,0,609,1,1,0,641,640,1376,0,0,0,0,608,1,1,1,1,0,0,32],"FLAG":0,"BASE":1}
    +

    M5Stack Atom lite~

    {"NAME":"M5Stack Atom Lite","GPIO":[1,1,1,1,1,1,1,1,1056,1,1,1,1,1,1,1,0,1,1,1,0,1,640,1376,0,0,0,0,608,1,1,1,1,0,0,32],"FLAG":0,"BASE":1}
    +

    LilyGO ttgo-t-eth-poe~

    {"NAME":"LilyGO ttgo-t-eth-poe","GPIO":[0,1,1,1,1,1,1,1,1,1,1,1,1,1,5600,1,0,1,1,5568,0,1,1,1,0,0,0,0,1,1,1,1,1,0,0,1],"FLAG":0,"BASE":1}
    +

    For working Ethernet, change the following setting in the Console:

    EthClockMode 1
    +

    these 3 devices are also fully supported, more detailed info will be added later

    TTGO ESP32 watch~

    fully supported with all sensors

    TTGO T4~

    fully supported

    m5stack CORE2~

    is fully supported with all sensors and SD card. Upload firmware Tasmota32-lvgl and configure via Menu Auto-configuration

    Heltec WiFi Kit 32~

    OLED display needs the following template

    {"NAME":"WiFi Kit 32","GPIO":[1,1,1,1,640,1,1,1,1,1,1,608,3840,1,1,1,0,1,1,1,0,224,1,1,0,0,0,0,1,1,1,1,1,0,0,1],"FLAG":0,"BASE":1}
    +

    WEMOS / LOLIN D32~

    verified support for board plus I2C sensors

    to use I2C, set IO21 to SDA and IO22 to SCL

    Displays, sensors and other options~

    displays: (most probably all I2C displays will work)

    USE_DISPLAY_SH1106  
    +USE_DISPLAY_EPAPER_29  
    +USE_DISPLAY_EPAPER_42  
    +USE_DISPLAY_ILI9341  
    +USE_DISPLAY_ILI9488  
    +USE_DISPLAY_SSD1351  
    +USE_DISPLAY_RA8876  
    +USE_DISPLAY_ST7789  
    +USE_DISPLAY_ILI9341_2  
    +USE_DISPLAY_ILI9342  
    +

    sensors: (most probably all I2C sensors will work)

    USE_SHT3X  
    +USE_BMP  
    +USE_VL53L0X  
    +USE_MLX90614  
    +USE_IBEACON  
    +USE_SML_M  
    +

    misc:

    USE_MP3_PLAYER  
    +USE_SCRIPT (scripting and all its options)  
    +USE_24C256  
    +USE_SENDMAIL
    +USE_ESP32MAIL
    +

    \ No newline at end of file diff --git a/ESP32/index.html b/ESP32/index.html new file mode 100644 index 0000000000..9678339003 --- /dev/null +++ b/ESP32/index.html @@ -0,0 +1,3 @@ + Tasmota32 Features - Tasmota
    Skip to content

    ESP32~

    ESP32-S2/S3 support is in beta and not all functions or supported peripherals will work reliably.

    Due to the scope and activity of development there might be breaking changes and incompatibilities between major and minor versions of Tasmota32. In case of problems first erase flash and serial flash the latest development binary.

    ESP32 Differences~

    All ESP32 systems on a chip (SoC) are 32-bit MCUs with 2.4 GHz Wi-Fi & Bluetooth/Bluetooth LE built in. There are distinct product lines which differ from each other in varying degrees. See ESP32 modules list for the full list.

    ESP32~

    An ESP32 has two or one Xtensa® 32-bit LX6 microprocessor(s) with clock frequency ranging from 80 MHz to 240 MHz. Tasmota32 is initially developed and tested with the dual core ESP32-D0WD-V3 and later expanded to include single core or PSRAM versions. Only first 4MB of PSRAM are useable (and reported) even if a bigger chip is connected (see here for more information).

    Single core SoCs do not work with standard binaries, for those use only tasmota32solo1.bin or compile your own binary using the tasmota32solo1 environment.

    ESP32-S2~

    A more cost-efficient version of ESP32, cut down to a single core and several dedicated hardware security features (eFuse, flash encryption, secure boot, signature verification, integrated AES, SHA and RSA algorithms). It has 43 available GPIOs. Product page for ESP32-S2

    Use tasmota32s2- binaries for this line of chips.

    ESP32-S3~

    Keeping the security improvements the S3 line now again features the dual core SoC with Bluetooth upgraded to V5 . Product page for ESP32-S3.

    Use tasmota32s3- binaries for this line of chips.

    ESP32-C3~

    Unlike previous versions, C3 is a single-core Wi-Fi and Bluetooth 5 (LE) microcontroller SoC based on the open-source RISC-V architecture. It is available as ESP32-C3-MINI-1 and ESP32-C3-WROOM-02 modules. Product page for ESP32-C3

    Use tasmota32c3- binaries for this line of chips.

    Flashing~

    Use Tasmota Web Installer to easily flash ESP32 devices.

    Other options include:

    ESP_Flasher for flashing an ESP32 or ESP82xx (Windows, MacOs or Linux (Ubuntu)).

    esptool.py - use the following command syntax:

    esptool.py write_flash 0x0 tasmota32.factory.bin
    +

    Use a proper power supply!

    ESP32 is power hungry and there's a high chance it will not be able to boot properly off the serial-to-USB power. Power it from a separate power supply that can provide at least 500mA.

    You can download precompiled binaries:

    OTA upgrade from older versions of tasmota32 might fail due to significant changes in partition tables.

    Exclusive Features~

    Autoconf~

    As devices get more complex, it was useful to find a simpler way for users to configure a device in one click, including Template information, Ethernet configuration, SetOptions and Berry drivers.

    After flashing Tasmota, open the web UI of the device and navigate to Configuration -> Auto-configuration. Select your device from the drop-down and click Apply Configuration.

    Configuration files are stored here: https://github.com/tasmota/autoconf

    To use it you need to have #define USE_AUTOCONF.

    Berry Scripting~

    ESP32 introduces Berry language as a more approachable scripting language. Berry is very powerful and you can even code an I2C driver using it.

    LVGL~

    Use LVGL in conjunction with Berry on devices with displays and touch displays to design your own UI.

    CPU Temperature Sensor~

    Tasmota will create an internal temperature sensor and display the values in the webUI and MQTT. The accuracy of this sensor varies a lot depending on the ESP32 chip involved and should not be taken as a reliable metric.

    Enable display of ESP32 internal temperature with SetOption146 1

    {"Time":"2021-01-01T00:00:00","ESP32":{"Temperature":41.7},"TempUnit":"C"}
    +
    You can deactivate sensor by using command SetSensor127 0

    DAC~

    DAC GPIOs are supported through Berry gpio module.

    Hall Sensor~

    ESP32 has a built in hall effect sensor that detects changes in the magnetic field in its surroundings. It is located behind the metal lid of the module and connected to GPIO36 and GPIO39.

    To enable set in module configuration or template:

    • GPIO36 as HallEffect 1
    • GPIO39 as HallEffect 2

    I2S~

    ESP32 contains two I2S peripherals. These peripherals can be configured to input and output sample data via the I2S driver. Read more...

    I2S is possible through I2S Audio or Berry gpio module.

    Touch Pins~

    ESP32 has 10 capacitive touch GPIOs. More on configuring and using them....

    Compiling~

    Uncomment the tasmota32xxx build you want to compile in platformio_override.ini. For example, uncommenting tasmota32 will build tasmota32.bin on the next Build task in Platformio.

    platformio_override.ini

    All binaries use user_config_override.h if it exists.

    Working Devices~

    Tasmota Supported Devices Repository has a more extenstive list of ESP32 based devices.

    \ No newline at end of file diff --git a/EZO/index.html b/EZO/index.html new file mode 100644 index 0000000000..dc9ed99a64 --- /dev/null +++ b/EZO/index.html @@ -0,0 +1,13 @@ + EZO sensors - Tasmota
    Skip to content

    EZO sensors~

    This feature is not included in precompiled binaries

    When compiling your build add the following to user_config_override.h:

    //  #define USE_EZOPH         // [I2cDriver55] Enable support for EZO's pH sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZOORP        // [I2cDriver55] Enable support for EZO's ORP sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZORTD        // [I2cDriver55] Enable support for EZO's RTD sensor (+0k2 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZOHUM        // [I2cDriver55] Enable support for EZO's HUM sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZOEC         // [I2cDriver55] Enable support for EZO's EC sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZOCO2        // [I2cDriver55] Enable support for EZO's CO2 sensor (+0k2 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZOO2         // [I2cDriver55] Enable support for EZO's O2 sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZOPRS        // [I2cDriver55] Enable support for EZO's PRS sensor (+0k7 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZOFLO        // [I2cDriver55] Enable support for EZO's FLO sensor (+0k4 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZODO         // [I2cDriver55] Enable support for EZO's DO sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZORGB        // [I2cDriver55] Enable support for EZO's RGB sensor (+0k5 code) - Shared EZO code required for any EZO device (+1k2 code)
    +//  #define USE_EZOPMP        // [I2cDriver55] Enable support for EZO's PMP sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)    
    +

    EZO is a series of chemical sensors provided by Atlas Scientific

    At this point in time, the following EZO sensors are supported:

    build #define Sensor ID Sensor Name
    USE_EZODO EZO-D.O. Dissolved Oxygen
    USE_EZOORP EZO-ORP Oxygen Reduction Potential
    USE_EZOPH EZO-pH pH
    USE_EZOEC EZO-EC Electric Conductivity
    USE_EZORTD EZO-RTD Temperature
    USE_EZOFLO EZO-FLO Flow Meter
    USE_EZOCO2 EZO-CO2 Carbon Dioxyde (gas)
    USE_EZOPRS EZO-PRS Pressure
    USE_EZOO2 EZO-O2 Oxygen (gas)
    USE_EZOHUM EZO-HUM Humidity
    USE_EZORGB EZO-RGB Color

    Configuration~

    All EZO devices must operate under the I2C mode to be compatible with Tasmota. One of the easiest way to achieve this is to short SDA/Tx to PGND while powering the device through VCC & GND. For more details, see Instructables.

    Set I2C mode

    Tasmota will automatically detect any new device that has an I2C bus address between 0x61-0x70 (which covers the default address range of all EZO devices). Tasmota supports any multiple of EZO devices (up to 16 of any kind). When more than one device is detected, the devices will be indexed starting at index 1 (ex: EZO-1) and ordered by their I2C address. If only one EZO device is connected, there will be no indexing of them. You may change the address of your device by sending an I2C command to the device. For example, to change the address of a single EZOpH sensor, the following command can be issued in the console: Sensor78 I^2^C,100

    Specific details about the list of commands available for your EZO device can be found in the device-specific datasheet on the Atlas Scientific's website. In order to individually address a single EZO sensor when more than one is detected, the index must be specified on the command by having "-#" before the other arguments: Sensor78 -2 I2C,101

    Calibration~

    Many EZO devices require calibration in order to accurately report values. Calibration can be easily achieved by issuing the appropriate commands and following the instructions in the EZO datasheets. One easy way to achieve this is to put the sensor in the baseline environment and use the web interface to see when the value stabilizes. Once it does, visit the console and issue the device-specific calibration command.

    Readings~

    Currently, Tasmota samples the sensor information at the rate of 1Hz (1 sample/second) irrespective of the EZO device.

    Some EZO devices support reporting different units. For example EZO-RTD supports reading the temperature in °C, °K, or °F. In order to save memory, Tasmota will only be able reading the default format (ie: °C). If you wish to see the value in another format, you can use Tasmota's ability to display Fahrenheit to do the conversion for you. For conversions that are not supported by Tasmota, you may use OpenHab, Home Assistant or any other system in conjunction with MQTT to do the cnoversion. In every case, there exists a simple formula that will allow you to convert from one format to another and the EZO device are merely utilizing that formula to return the value. It's important to make sure that the default units are used. If that's not the case, Tasmota will simply assume that the default units are used and the number will seem drastically wrong.

    Some device supports the output of multiple different sensors in one single EZO devices. One example of this is the EZO-HUM which supports outputting the humidity as well as the temperature. By default, only the humidity is enabled, but a command can be sent to enable the temperature as well. In that case, Tasmota does support reading the additional sensor provided that the command enables its output.

    Some readings can be made more precise by specifying additional parameters. The most common one is temperature. For example, the EZO-pH sensor can return more accurate values if it compensates for the temperature. At this point in time, Tasmota does not support reading sensor data which compensates with any other sensor's data. That said, these are typically well-established equations that will allow you to convert the existing reading and adjust it based on the reading from the other sensor. A simple Google search can be done to understand how to compensate in each case.

    Sleep mode for EZO devices is currently NOT supported by Tasmota.

    \ No newline at end of file diff --git a/Energy-Saving/index.html b/Energy-Saving/index.html new file mode 100644 index 0000000000..c944dff391 --- /dev/null +++ b/Energy-Saving/index.html @@ -0,0 +1 @@ + Energy Saving - Tasmota
    Skip to content

    Energy Saving

    Using the Sleep command you can instruct Tasmota to sleep for the set milliseconds in its main cycle. While sleeping your device will consume less power.

    Dynamic Sleep is enabled by default with a value of 50. To continue using Normal Sleep you may disable it by using the command:

    SetOption60 1

    Setting Sleep to lowest value 1 reduces power consumption already about ~30% in idle mode (relay off) and button presses are still noticed without any delay. Setting this around 50 ms reduces power consumption from ~1.1 W to ~0.6 W on an idling (relay off) device and button presses are still noticed correctly. With this setting you have to concentrate very hard to click the button so fast that it is not recognized by the device.

    If your device needs to do something continuously, this will be affected. E.g., device LED will flicker and Sonoff POW will not be able to correctly total the energy consumption. Spot readings of power, voltage, etc. will however remain correct.

    From the release notes: Expect overall button/key/switch misses and wrong values on Sonoff Pow

    Notes: - Starting with Tasmota 6.1.1.12 Sleep0 has no impact on real time related commands like Delay, PulseTime, TelePeriod and Blinktime. - As the WiFi Modem on an ESP8266 is the major consumer - using Sleep with WiFi AP mode enabled is more or less without effect.

    Device power consumption and measurement~

    Most low-price plug-in home energy meters like Sonoff devices are very imprecise for power consumption < 10 W and become more and more imprecise for power consumption (< 1.5 W). Furthermore, the results of such meters are very strongly dependant on the load type (capacitive/inductive) and are absolutely imprecise for non-ohmic load types having switch-mode power supplies.

    In addition, using Sleep - which periodically cycles the power of the device between 20% and 100% - on such meters makes their readings more or less useless.

    Example of power consumption~

    Absolute AC line measurement using calibrated meter~

    The following measurement was done directly on 230 V/AC line using a Sonoff Dual R2 and two different Sonoff S20 connected on different MID calibrated meter (Eastron SMD630 MID/saia-burgess ALE3).

    Device power consumption using Sleep~

    Device Sleep 0 Sleep 1 Sleep 50 Sleep 200
    Sonoff Dual R2 1.24 W 0.84 W 0.76 W 0.68 W
    Sonoff S20 1.11 W 0.77 W 0.59 W 0.51 W

    All measurements were done with - WiFi STA mode, enabled and connected (70%) - MQTT enabled and connected - Syslog enabled and connected - TelePeriod 60 for debugging - Relays off - Measurement period of 24-56 hours to reduce measurement discrepancies

    The first impression is that a higher sleep value reduces the consumption, but only slightly. The second result is that using Sleep <value> (value ≠ 0, e. g., 1) is good enough to reduce the power consumption anywhere between 1/3 and up to 45% (value=200).

    Quantity measurement of ESP-12 module/ESP8266 3.3V line~

    To find out why Sleep reduces the power consumption and how its value should be set, the current of the 3.3V DC ESP8266 of an ESP-12 module was measured using an oscilloscope and (for easy reading the oscilloscope diagram) a 1 Ω shunt which results in a 1:1 interpretation between voltage and current.

    This measurement simplified the measure of a time based power consumption; no integration must be done. On the other side note that the quantity measurement does not respect the effectiveness of the complete device circuit.

    Sleep 0~

    Using Sleep 0 there are no delay() calls in Tasmota main loop and therefore the power consumption is continuous at current ~80 mA: sleep 0

    Sleep 1~

    Due to the fact that the Tasmota main loop now calls delay() (even with 1ms) it seems it results in periodically (100ms) enabling the WiFi Modem Sleep mode within the WiFi Manager library. It results in periodically lowering the current to 15-20 mA for ~90ms of each loop: sleep 1

    Sleep 100~

    By increasing the sleep value, there are more and more ~90ms periods with additional lowering of the current to 8-10 mA - I really don't know where this comes from: sleep 100 1

    Sleep 250~

    As already noticed with Sleep 100 the number periods having 8-10 mA instead of 15-20 mA increase again: sleep 250 1

    The quantity measurement confirms the suspicion that a Sleep value ≠ 0 results in reducing the power consumption about 1/3.

    \ No newline at end of file diff --git a/Esptool/index.html b/Esptool/index.html new file mode 100644 index 0000000000..1e98075ec1 --- /dev/null +++ b/Esptool/index.html @@ -0,0 +1,4 @@ + Esptool - Tasmota
    Skip to content

    Esptool

    How to setup and configure Esptool for Tasmota upload.

    The information below is for the Python version of esptool - If you want to use the Windows/Linux/OSX(MAC) executable version of esptool (as would be included in Arduino ESP8266 cores) then please go to the esptool executable (Windows & Linux) section at the bottom.

    Download Esptool~

    If you do not have an installed copy of Python download and install it from https://www.python.org/.

    Download Esptool Source code from https://github.com/espressif/esptool/releases to a known folder.

    Install Esptool~

    Go to the known folder and install Esptool with command python setup.py install.

    Packages for Esptool are maintained for Debian and Ubuntu and can be installed with sudo apt install esptool.

    Download Tasmota~

    Download the latest Tasmota release firmware file tasmota.bin to a known folder.

    Upload Tasmota~

    Put device in firmware upload mode~

    When performing a firmware upload do not connect the device to AC but use the power supply provided by your (FTDI type) serial interface.

    Put the device in firmware upload mode by grounding pin GPIO00 while applying power.

    Grounding pin GPIO00 can often be achieved by pressing button 1 on the Sonoff device or using a wire between GPIO00 and Gnd if the button is not available. Deviations may apply.

    Connect the serial interface of your PC to the device while GPIO00 to Gnd.

    Esptool uses the serial interface to communicate with your device. On Windows these interfaces are named COM1, COM2 etc. On Linux these interfaces are called /dev/ttyUSB0, /dev/ttyUSB1 etc.

    Before using Esptool make sure you know to which serial interface name your device is connected to.

    In the following commands I use COM5 as an example.

    Optional: Backup firmware~

    Ensure the device is in firmware upload mode.

    Backup the current firmware with the following command:

    esptool.py --port COM5 read_flash 0x00000 0x100000 image1M.bin
    +
    NOTE: When the command completes the device is out of firmware upload mode!

    Erase firmware~

    Ensure the device is in firmware upload mode.

    Erase the complete flash memory holding the firmware with the following command:

    esptool.py --port COM5 erase_flash
    +
    NOTE1: When the command completes the device is out of firmware upload mode!

    NOTE2: It only takes a few seconds to erase 1M of flash.

    Upload firmware~

    Ensure the device is in firmware upload mode.

    Load the downloaded Tasmota firmware file tasmota.bin with the following command:

    esptool.py --port COM5 write_flash -fs 1MB -fm dout 0x0 tasmota.bin
    +
    NOTE1: When the command completes the device is out of firmware upload mode!

    NOTE2: For a proper device initialization after first firmware upload power down and power up the device.

    ESPTOOL Executable (Windows, MacOs and Linux)~

    The executable version of esptool can be downloaded from https://github.com/espressif/esptool/releases

    Download and Install~

    For the purpose of simplicity only the Windows version will be explained here, but the command and parameters are the same for Windows, Linux and MAC/OSX.

    Download the latest release from https://github.com/espressif/esptool/releases and extract the compressed file to a known location.

    Download Tasmota~

    Download the latest Tasmota release firmware file tasmota.bin to a known folder (The same folder as where you have the esptool executable will work well for this process to be simpler)

    If you want features from the current development codebase which has not been included in the last release please download this tasmota.bin to a known folder (The same folder as where you have the esptool executable will work well for this process to be simpler)

    The information posted further up in this Wiki for placing the device into bootload / firmware upgrade mode may be followed as this process does not change irrespective whether you use the Python or executable version of esptool.

    Once the device is in firmware upload mode the following commands are recommended for completion of the firmware flashing.

    Erase the flash completely with the following command (substituting the COM port for the one which was used on your computer)

    esptool.exe --port COM5 erase_flash

    Once the erase is complete, reset your device back into programming mode and then upload the firmware using the following command

    esptool.exe --port COM5 write_flash -fs 1MB -fm dout 0x0 tasmota.bin

    \ No newline at end of file diff --git a/Expanding-4CH/index.html b/Expanding-4CH/index.html new file mode 100644 index 0000000000..10260b174c --- /dev/null +++ b/Expanding-4CH/index.html @@ -0,0 +1 @@ + Expanding 4CH - Tasmota

    Expanding 4CH

    Opening my Sonoff 4CH (https://www.itead.cc/sonoff-4ch.html) I realized that on PCB there is a predisposition for 2.5 mm jack (also confirmed by schematic on Itead wiki: https://www.itead.cc/wiki/File:Sonoff_4CH.SCHMATIC.pdf).

    I ordered on Aliexpress some 2.5 mm jacks and when I received it after some work...

    The schematic reports that 2.5 mm jack is configured as following (jack on right side, ESP8285 on the left side:

    3.3V and GND are very welcome for sensors expansions but GPIO7 and GPIO8 are not usable because they are used internally by ESP8285 for flash memory connection (sigh!)

    R35 and R36 resistors are not soldered on Sonoff 4CH PCB and it is possible to use their pads to "manually route" the jack contact on another ESP8285 pin (!). I selected R35 pad because my "waterproof" DS18B20 sensors provide signal line on TEM1 jack contact. For ESP8285 pin I prefer IO2 that is already available on header programming pins...

    Then the route to do is: TEM1 -> R35 pad -> GPIO2:

    DS18B20 sensors need a 4.7Kohm pullup resistor between DATA pin and 3.3V and this is also useful to pullup GPIO2 in order to avoid ESP8285 reset.

    Final result in these photos:

    Tasmota is also configured with DS18x20 on GPIO2 and it works !

    *Note*: source file sonoff/sonoff_template.h provides the configuration on Sonoff 4CH and it is also possible to use GPIO16 (as in the screenshot above) but be aware that no interrupts can be used on GPIO16 and you could have problems with some sensors.

    \ No newline at end of file diff --git a/Expanding-Tasmota/index.html b/Expanding-Tasmota/index.html new file mode 100644 index 0000000000..59025d2edc --- /dev/null +++ b/Expanding-Tasmota/index.html @@ -0,0 +1 @@ + Expanding Tasmota - Tasmota
    Skip to content

    You can wire peripherals (sensors, displays, switches, LED lights, ...) to available pins of the ESP8266 chip that controls these devices.

    To make a link between the different naming schemes the Pin Definition overview in the ESP8266 wiki is quite helpful.

    Examples~

    Connect switch~

    If you take a Sonoff Basic and connect a switch between pin4 (GND) and pin5 (GPIO14) of the 5 pin programming header you now have a second switch connected to the device. You can set this through the module config page as option Switch1 (9) or from the command line with gpio14 9.

    See Buttons and Switches for more information.

    Connect jack~

    Instead of connecting a switch, you could connect a 4-pin 2.5mm jack, with the pins wired:

    Jack Pin ESP8266
    tip 5 GPIO14
    R1 no connection
    R2 1 GND
    R3 4 3.3V

    You can then plug a sensor into the jack like you would to a Sonoff TH and define what sensor you have connected to GPIO14.

    Restrictions~

    Danger

    If you can avoid it, don't use GPIOs: 0, 1, 2, 6-11, 15 and 16. That leaves 4, 5, 12, 13, 14 as GPIOs without any constraints. 3 being RX is also good to avoid (PWM is not working on this GPIO).

    Others can be used but you have to mind the constraints outlined in this document.

    Voltage and Current~

    Danger

    The ESP8266 is a 3.3V microcontroller, so its I/O operates at 3.3V as well. The pins are not 5V tolerant, applying more than 3.6V on any pin will release the magic smoke (fry the chip).

    The maximum current that can be drawn from a single GPIO pin is 12mA.

    Power Supply~

    Danger

    The power supplied to the device is one of the most important elements for stable device operation. Many devices on the market have barely adequate power supplies for normal operation. Connected peripherals may strain the ability of the power supply on the device to deliver appropriate power to all the components, both on-board as well as externally connected.

    Voltage regulation issues typically result in fatal exception fault code 1. You must ensure that the device receives sufficient power (current and appropriate voltage level). Take into account the current that each wired component (f.e. sensor) will draw from the device itself.

    Electrical Considerations~

    When you switch a GPIO pin to an input and hang a long wire off of it, that wire can pick up stray signals and cause the voltage on the GPIO pin to vary. This can cause the system to think the switch has changed.

    To fix this, there are several things you can do.

    1. add a pull-up resistor
    2. add a bypass capacitor
    3. shielding on the wire
    4. use twisted pair wiring

    A pull-up resistor is a resistor connected between the GPIO pin and 3.3v. The exact value of this is not critical, 4.7k is a common value to use, as is 10k. This ensures that when the switch it open, the GPIO pin will go high.

    A bypass capacitor is a small (pF range) capacitor that is connected between the GPIO and ground. This provides a path for any radio signals that are picked up by the wire to go to ground and not confuse the system.

    Shielding or using twisted pair wiring are other ways to reduce the effect of radio signals on the system.

    Example for 10K Resistor (issue#2708)

    The ESP8266 Hardware~

    Complete document available from https://tttapa.github.io/ESP8266/Chap04%20-%20Microcontroller.html

    Digital I/O~

    Just like a normal Arduino, the ESP8266 has digital input/output pins (I/O or GPIO, General Purpose Input/Output pins). As the name implies, they can be used as digital inputs to read a digital voltage, or as digital outputs to output either 0V (sink current) or 3.3V (source current).

    Usable pins~

    The ESP8266 and ESP8255 have 17 GPIO pins (0-16) but several are reserved or have constraints. Do not use any of the reserved pins. If you do, you might crash your program. On the ESP8266, six pins (GPIO 6 - 11) are used to interface the flash memory (the small 8-legged chip usually right next to the ESP8266). The ESP8255 has its flash memory integrated into the chip which frees up GPIO 9 and 10.

    GPIO 1 and 3 are used as TX and RX of the hardware Serial port (UART), so in most cases, you can’t use them as normal I/O while sending/receiving serial data.

    Boot modes~

    Some I/O pins have a special function during boot: They select 1 of 3 boot modes:

    GPIO15 GPIO0 GPIO2 Mode
    0V 0V 3.3V Uart Bootloader
    0V 3.3V 3.3V Boot sketch (SPI flash)
    3.3V x x SDIO mode (not used for Arduino)

    Note: you don’t have to add an external pull-up resistor to GPIO2, the internal one is enabled at boot.

    We have to be sure that these conditions are met by adding external resistors, or the board manufacturer of your board has added them for you. This has some implications, however:

    GPIO15 is always pulled low, so you can’t use the internal pull-up resistor. You have to keep this in mind when using GPIO15 as an input to read a switch or connect it to a device with an open-collector (or open-drain) output, like I²C. GPIO0 is pulled high during normal operation, so you can’t use it as a Hi-Z input. GPIO2 can’t be low at boot, so you can’t connect a switch to it. Internal pull-up/-down resistors GPIO 0-15 all have a built-in pull-up resistor, just like in an Arduino. GPIO16 has a built-in pull-down resistor.

    PWM~

    Unlike most Atmel chips (Arduino), the ESP8266 doesn’t support hardware PWM, however, software PWM is supported on all digital pins. The default PWM range is 10-bits @ 1kHz, but this can be changed (up to >14-bit@1kHz). Check Restrictions.

    Analog input~

    The ESP8266 has a single analog input, with an input range of 0 - 1.0V. If you supply 3.3V, for example, you will damage the chip. Some boards like the NodeMCU have an on-board resistive voltage divider, to get an easier 0 - 3.3V range. You could also just use a trimpot as a voltage divider.

    The ADC (analog to digital converter) has a resolution of 10 bits.

    Communication~

    Serial~

    The ESP8266 has two hardware UARTS (Serial ports): UART0 on pins 1 and 3 (TX0 and RX0 resp.), and UART1 on pins 2 and 8 (TX1 and RX1 resp.), however, GPIO8 is used to connect the flash chip. This means that UART1 can only transmit data.

    UART0 also has hardware flow control on pins 15 and 13 (RTS0 and CTS0 resp.). These two pins can also be used as alternative TX0 and RX0 pins.

    I²C~

    ESP8266 doesn’t have a hardware TWI (Two Wire Interface) but it is implemented in software. This means that you can use pretty much any two digital pins. By default, the I²C library uses pin 4 as SDA and pin 5 as SCL. (The data sheet specifies GPIO2 as SDA and GPIO14 as SCL.) The maximum speed is approximately 450kHz.

    SPI~

    The ESP8266 has one SPI connection available to the user, referred to as HSPI. It uses GPIO14 as CLK, 12 as MISO, 13 as MOSI and 15 as Slave Select (SS). It can be used in both Slave and Master mode (in software).

    GPIO overview~

    NodeMCU Labelled Pin GPIO# Function State Restrictions
    D3 0 Boot mode select 3.3V No Hi-Z
    D10 1 TX0 - Not usable during Serial transmission
    D4 2 Boot mode select TX1 3.3V (boot only) Don’t connect to ground at boot time Sends debug data at boot time
    D9 3 RX0 - Not usable during Serial transmission
    D2 4 SDA (I²C) - -
    D1 5 SCL (I²C) - -
    x 6 - 8 Flash connection x Not usable, and not broken out
    x 9, 10 Flash connection * Only available on the ESP8285
    x 11 Flash connection x Not usable, and not broken out
    D6 12 MISO (SPI) - -
    D7 13 MOSI (SPI) - -
    D5 14 SCK (SPI) - -
    D8 15 SS (SPI) 0V Pull-up resistor not usable (extern pull down resistor)
    D0 16 Wake up from sleep - No pull-up resistor, but pull-down instead Should be connected to RST to wake up
    \ No newline at end of file diff --git a/FAQ/index.html b/FAQ/index.html new file mode 100644 index 0000000000..79cc339f83 --- /dev/null +++ b/FAQ/index.html @@ -0,0 +1,18 @@ + FAQ - Tasmota
    Skip to content

    FAQ

    Wi-Fi~

    Cannot connect to Wi-Fi~

    If your device does not connect to your Wi-Fi and you've made sure the Wi-Fi credentials are correct, it is caused by using special chars or white spaces in your SSID or Password of your Wi-Fi. Remove them and try again. Other reason can be using an SSID longer than the allowed 32 characters.

    With some Wi-Fi routers (i.e. Linksys with DD-WRT), you may have conflicts with the 5GHz radio. Don't choose "Mixed" option. Select "AC/N-Mixed" instead. Moreover, you probably should disconnect 5GHz radio during the configuration process.

    DD-WRT also has Wi-Fi Multi-Media (WMM) enabled by default. Disabling WMM can resolve connectivity issues.

    Some Tasmota devices also have issues with OFDMA (WiFi 6) enabled on the 2.4GHz band. Disabling OFDMA can solve problems with WiFi failing to connect.

    I entered wrong Wi-Fi information~

    If you have a device with a button and the button is configured as a component in the Tasmota settings (e.g., GPIO0 - Button1), you can try pressing the button to force the device into Wi-Fi configuration mode with 4 short presses of the button. Note: Since version 8.3.0 this requires 6 short presses instead.

    If that didn't work reset your device using Fast power cycle device recovery

    If you are unsure what SSID you have entered, you can try to find that with special Wi-Fi sniffing tools. For example Nirsoft WifiChannelMonitor can show your mistakenly configured SSID name.
    Linux system example:

    apt install aircrack-ng wireshark
    +airmon-ng check kill
    +airmon-ng start (e.g. wlp58s0 or wlan0)
    +wireshark
    +
    Select your Wi-Fi device from the list. Plug in the misconfigured device and immediately watch SSIDs. You should see your misconfigured SSID fairly soon.

    If these methods don't work, it may still be possible to save the device without opening it to perform a serial flash. Since Tasmota uses GET request for forms, the password may be in your browser history.

    1. Search in your browser history for 192.168.4.1 (or whatever address you used for configuring it)
    2. There should be an entry similar to this:
      http://192.168.4.1/wi?s1=<mySSID>&p1=<myPassword>-********&s2=&p2=********&h=hostName&save=

      • s1 is your first AP SSID
      • p1 is the first AP password
      • s2 and p2 are the same parameters but for the second AP
      • h is the hostname given to the device by the Tasmota configuration
      • After getting the incorrectly entered configuration from this URL, configure an access point with these settings as described above
      • Access your device and set the correct Wi-Fi credentials

    If you flashed a light bulb or any device without a built-in button and entered wrong Wi-Fi password you now have a device that won't connect to your Wi-Fi and you have no button to force it into Wi-Fi configuration mode.

    This tip takes advantage of a security risk present in Arduino Cores prior to 2.6.0. It will not work with Tasmota binaries compiled with 2.6.0 or later.

    To solve this you can try creating a new Wi-Fi AP with the same SSID and no (none) authentication. Use an old router, a mobile phone or, if you're desperate, change the settings on your main router (but remember to turn authentication back on when you're done). Depending on the router/phone it will ignore the wrong Wi-Fi password since authentication is set to none and let your Tasmota flashed device connect to it.

    Now simply connect to the same AP and open the web UI, triple check your ssid and password, enter some simple info for SSID2 which you can create as a hotspot on your phone and save.

    Device disconnects from Wi-Fi often~

    First thing to try when having Wi-Fi issues: Reset 3 which will erase wi-fi calibration data only and will keep configuration intact. Make sure to power cycle restart after that. If that doesn't help try rebooting the router as well

    As a last resort try : Erase all flash using esptool.py or esptool.exe and flash via serial (as explained here) using the latest precompiled binaries.

    This approach has solved many of the reported issues. Sometimes this is due to a bad flash, a bad OTA or invalid data that remains in the flash where the SDK memory is.

    If you still have issues, you should look into your Wi-Fi network:

    • Some new routers have many modern features enabled with default which don't work well with the old ESP82xx chip. Disable any channel surfing, band changing and similar features.
    • Check the Wi-Fi channel availability and noise with an Android app like Wi-Fi Analyzer. Disable Auto Channel in your Wi-Fi router and select any Wi-Fi channel that is not very congested in your area.
    • Disable Wi-Fi Repeaters and Mesh Networks.
    • Check Wi-Fi signal in your device.

    The same Mesh may be stable in one area and lead to unwanted Tasmota reconnects in other areas, presumably when the signals of access points overlap with similar strength. If disabling Mesh Networks is not an option, then keeping the network busy, e.g. by issuing a Ping from another host every 20 seconds has helped to avoid the reconnects.

    Wi-Fi Stops Working~

    There have been many reports of Wi-Fi no longer working after it was working for a while.

    Every time this has been reported, it's ended up being a hardware or signal interference problem.

    On the hardware side, we've seen reports of bad solder joints on the board that when touched up seem to solve the problem (capacitors being loose can cause this) or low quality/weak power supplies or voltage regulators that cannot cope with the power requirements of Tasmota or have degraded over time.

    We've also seen reports then when a specific LED light bulb was hooked up near one, the signal quality dropped to unusable.

    All you can really do is check the solder joints, move the device closer to your Access Point. If all else fails, replace the device.

    Weaker Wi-Fi signal after upgrade~

    On an ESP82xx, Wi-Fi calibration is sensitive to the power supplied. If this changes substantially (e.g., you add a sensor, configure a new Tasmota feature, upgrade the firmware or Arduino Core, etc.), the device's Wi-Fi calibration may not be set properly any longer. The Wi-Fi signal strength (RSSI) can drop significantly and impact Wi-Fi performance. In such a case, the Wi-Fi calibration needs to be deleted to force the device to re-calibrate Wi-Fi after it restarts.

    1. Run Reset 3 in the Console.
    2. The device will restart
    3. Cycle the power on the device. Wi-Fi calibration will not be done unless the device performs a cold boot from power up.

    WebUI unavailable but device can be controlled~

    Some routers have issues with ARP implementation. To help with that use SetOption41 to make your device send grauitous ARP in a desired interval (try 30 or 60 seconds for start).

    MQTT~

    Cannot connect to my MQTT broker~

    Make sure you've configured MQTT correctly. If that didn't solve the issue check your MQTT broker logs. Most likely problem is your broker doesn't allow logins for your Tasmota configure user and password or your ACL settings do not include your device.

    In some very specific cases the MQTT broker code clashes with the Arduino Core and doesn't allow a connection. In that case create a different user for your device, try another core binary or a different MQTT broker.

    Frequent MQTT reconnects~

    Most MQTT reconnect messages are linked with Wi-Fi instability first. Resolve any Wi-Fi issue first!

    If the console shows repeated messages like:

    02:32:54 MQTT: tele/MYSONOFF/LWT = Online (retained)
    +02:32:54 MQTT: cmnd/MYSONOFF/POWER = 
    +02:32:55 MQTT: Attempting connection...
    +02:32:56 mDNS: Query done with 0 mqtt services found
    +02:32:56 MQTT: Connected
    +
    or your mosquitto broker log shows messages like this -
    1496455347: New client connected from IP_addr_1 as SONOFF (c1, k15, u'SONOFF_USER').
    +1496455349: New connection from IP_addr_1 on port 1883.
    +1496455349: Client SONOFF already connected, closing old connection.
    +1496455349: Client SONOFF disconnected.
    +1496455349: New client connected from IP_addr_2 as SONOFF (c1, k15, u'SONOFF_USER').
    +1496455350: New connection from IP_addr_2 on port 1883.
    +1496455350: Client SONOFF already connected, closing old connection.
    +1496455350: Client SONOFF disconnected.
    +
    You have more than one device connected with the same %topic% defined. It's important that each device has a unique %topic% instead of the default sonoff.

    If that is not the issue, erase all flash using esptool.py or esptool.exe and flash again by wire (as explained here) using the latest precompiled bins with core v2.7.1.

    Device is often unavailable, usually 30 seconds every minute~

    Due to a bug in the Arduino core that we are working on to solve, Tasmota devices require time to be correctly setup to work properly (see https://github.com/tasmota/docs/blob/master/docs/FAQ.md#timers-trigger-at-the-wrong-time for details and alternatives). If a Tasmota device cannot reach one of the NTP server after restarting, the device will be unavailable during 30 seconds every minute while trying to update the time.

    To confirm this issue, the command weblog can be set to 4 (typing "weblog 4" in the console), and log should indicate "NTP: Sync time..." followed by "NTP: Unable to resolve IP address" 30 seconds later, in the 30 seconds period the device is unavailable.

    Configuration~

    Device reset to defaults on its own~

    Bad power supply~

    Most common culprit is Power Cycle Recovery which can be activated if the device has a bad power supply or your power grid has fluctuations/brownouts. Disable the feature with SetOption65 1

    Button in ON state when depressed~

    If a button is configure to be in ON state when depressed it will activate "Firmware Reset" feature. Either change the button mode or use SetOption1 1 to disable factory reset mode.

    Frequent reboots/bootloops~

    Your device may be in a boot loop - a restart caused by any exception or watchdog timer within less than BOOT_LOOP_TIME (default 10 seconds). The number of boot loops allowed before beginning to reset settings is determined by SetOption36. When Tasmota reaches this situation, it will begin restoring default settings as follows:

    • 1st restart: disable ESP8285 generic GPIOs interfering with flash SPI
    • 2nd restart: disable rules causing boot loop
    • 3rd restart: disable all rules
    • 4th restart: reset user defined GPIOs to disable any attached peripherals
    • 5th restart: reset module to Sonoff Basic (1)

    Relay clicks and LED flashes at 1 second intervals~

    This indicates that your device did not get flashed properly. In this case it will toggle all its pins at 1 sec intervals. A flash erase and a new flash is required.

    Status LED blinking~

    Your device status LED blinks repeatedly when Wi-Fi and/or MQTT is not connected. If you're not using MQTT and did not configure it the status LED will still keep blinking.

    You can disable status LED blinking using: Backlog LedPower 0; SetOption31 1

    My device randomly switches on and off. Do I have ghosts in my house?~

    Most of the issues with random, or "ghost", switching are related to MQTT retain settings. In short, your MQTT broker is retaining a message with the POWER status of the device which gets applied on reboots. Solution here

    In some cases, adding a switch to a device causes ghost switching. In this case, you may need to add a low pass filter to dampen any spikes on the input. In the case of the Sonoff T1, a modification to change the filter capacitor on the PCB may be required.

    This short 10 minute video by TheHookUp nicely explains what it is and how to prevent it.

    Other cause can be of electrical nature. If you have connected an external switch using long wires they can pick up stray signals and cause the voltage on the GPIO to vary. Solution here

    Cannot find my device in Modules~

    If you flashed a device which is not listed in the Modules list, use Templates to configure your device. Try looking for it first in the Templates Repository.

    Device keeps restarting after changing config over MQTT~

    If you changed configurations over MQTT, the command can fail due to a bug and the command is repeatedly sent, causing the device to restart.

    The restart is normal if you change something at the device configuration.

    You need to clear the retain messages of your HA/Broker/MQTT Server.

    Read also:

    Tasmota is sending many status updates every 5 seconds~

    Turn off TasmoAdmin! It is polling your device with STATUS 0 command with a HTTP request every 5 seconds which causes the status updates and unnecessary stress load on the device. In some cases it might even interfere with normal device operation.

    Web Interface Asks for Password~

    You modified the Web Admin password (Configure Other) and now you cannot access the web interface. You have set up a password for the web interface. You can login with the username admin and the password you entered. However, if you don't remember that password there are a few options you can try to gain access to the web interface again.

    1. Reset the password using the WebPassword command.

    2. If you have serial connection to the device: Execute WebPassword 0 using a serial terminal interface.

    3. If you have configured MQTT: Send 0 to cmnd/<device-topic>/WebPassword. You can send it from any MQTT client. You can also use another Tasmota device using the Publish command - Execute Publish cmnd/<device-topic>/WebPassword 0 from that device's Console.

    4. If the options above are not available: Since Tasmota uses GET request for forms, the password may be in your browser history. Look there for entries with the name you configured for the device. For example, in the following link:

    http://<device-ip>/co?t1={"NAME":"Generic"'"GPIO":[23'22'24'17'134'132'0'0'131'52'21'0'0]'"FLAG":0'"BASE":67}&p1=SecretPassword&b1=on&a1=Sonoff&a2=Sonoff2&a3=Sonoff3&a4=Sonoff4&b2=0&save=

    the p1 parameter contains the password for the web interface (SecretPassword in this case).

    Note: special characters may appear as the characters' corresponding ASCII hexadecimal codes (e.g., "{" = '\%7B', etc.)

    1. If you had set up WifiConfig 7 as your Wi-Fi fallback method (by previously executing WiFiConfig in the Console), you can reset the device by booting it into Wi-Fi Manager mode. If the SSID configured in the device is not available (e.g., turn off the router), the device will fallback to that restricted Wi-Fi Manager Mode.

    2. If your device has a physical push-button, reset the firmware to the default settings as detailed here.

    3. If nothing helps, then you have to flash the firmware again using the serial interface. Be sure to erase the flash memory before uploading the binary.

    Power monitoring shows wrong values~

    If the values shown in the Web UI don't seem right and you're using a Supported Module you need to calibrate the power monitoring sensor.

    In case you're using a template you created yourself or found in our Templates Repository try the calibration method first. If the values are still wrong or unrealistic the power monitoring sensors' GPIOs are not configured correctly and you will need to find the correct GPIO assignments before proceeding.

    Sensors do not show values~

    Make sure your sensor is properly wired and the GPIOs assigned. Your vanilla tasmota.bin doesn't have complete sensor support. Make sure you've installed tasmota-sensors.bin that support the largest number of sensors. Some sensors require enabling in the code and compiling your own binary. See Firmware-Builds for a comprehensive list of supported components.

    Timers trigger at the wrong time~

    Tasmota devices must have a their time of day set properly in order for any timers to work properly. Check the log in the web UI Console to see if the device's time is set correctly. There are two elements to setting the time: 1. obtaining the UTC time, and, 2. local Daylight Saving Time policies.

    There are three methods available to set the device time: 1. NTP, 2. An RTC peripheral, or 3. the Time command. The typical method Tasmota uses to set its time is to obtain the time from an Internet NTP server. It can also query an NTP server on its local network (e.g., a network router with an NTP service, a Raspberry Pi running the NTP daemon, the Chrony add-on in Home Assistant, etc.).

    The time sync request is forced to NTPSERVER1. If can't connect, it tries NTPSERVER2. And finally NTPSERVER3. Ensure that these parameters are set appropriately and that the device can reach at least one of these time servers. You may want to consider setting up an NTP server locally. As long as the computer is able to set its time at some point from an Internet time server, this computer can serve as an NTP server for your Tasmota device(s). This can be the same computer that hosts your MQTT broker or home automation hub.

    You must also set the TimeZone and Daylight Saving Time policies (TimeDST/TimeSTD).

    If you have timers that use the sunset or sunrise times, you must set your latitude and longitude in order for these times to be calculated correctly for your location.

    Auto-discovery in Home Assistant does not work~

    The tasmota-lite.bin firmware binary (which comes packaged with Tuya-Convert) does not support auto-discovery. Please upgrade to tasmota.bin or a similar firmware variant that supports this feature.

    Make sure it's enabled in Tasmota it with SetOption19 1 and you configured the Home Assistant MQTT integration with Discovery enabled.

    Why is my changed configuration not loaded?~

    If you have flashed a precompiled binary, be aware that all the configuration made after the flash (Wi-Fi, MQTT, topics, names, rules, etc) will be lost in a factory firmware reset.

    In short: The CFG_HOLDER is the place where the config is stored on your device. The device checks if a config is saved in this CFG_HOLDER (value from the my_user_config.h) and always loads this if exists. => won't load new applied configs in your my_user_config.h

    To get the new config on your device, you need to change the CFG_HOLDER. BUT: You should always try to stay on the default CFG_HOLDER, to reach this, you need to flash two times

    • change your config in the my_user_config.h or better user_config_override.h
    • change the CFG_HOLDER number. +1 or -1 is enough (e.g. 0x20161208)
    • flash
    • change the CFG_HOLDER back to default ( 0x20161209 )
    • flash again

    After this, your new config is saved in the default CFG_HOLDER on your device.

    This is necessary to avoid losing your config if you update to a new firmware by using the pre-build images or if you forget to change the CFG_HOLDER to your custom one if you build the firmware yourself.

    How CFG_HOLDER works: The config of your Tasmota is stored in an area of the flash memory (flash config area or FCA). Using a new device (where Tasmota firmware runs the first time) the FCA does not contain a Tasmota configuration so on the very first start of Tasmota it uses your settings from my_user_config.h or user_config_override.h and copy this into the FCA. To prevent the following Tasmota starts from overwriting your FCA settings again (e.g. because you changed some things using commands), the FCA will be marked by a header value indicating not to copy the values from my_user_config.h/user_config_override.h again. This header becomes the value from CFG_HOLDER.

    On every start the device compares the header of FCA with the CFG_HOLDER from your source code and only if this header value is not identical, Tasmotat will copy the data from my_user_config.h/user_config_override.h to flash settings area - this is normally only the case on a fresh device or if you has changed the CFG_HOLDER value.

    Summary: To force Tasmota to overwrite current (valid or invalid) settings in FCA with your settings from my_user_config.h/user_config_override.h you can

    • change CFG_HOLDER value once, compile, reflash device (as described above). To avoid overwriting settings by new versions don't forget either
    • repeat the step above using original CFG_HOLDER value
    • or never forget to change CFG_HOLDER value for even all upcoming version to your value
    • or use the command Reset 1 or Reset 2 after changes in your my_user_config.h/user_config_override.h without the need to double reflash your device and/or double change your CFG_HOLDER value:
    • change values in my_user_config.h/user_config_override.h
    • leave CFG_HOLDER as is
    • start your device and issue command Reset 1 or Reset 2

    How do I invert the output of the green LED on the Sonoff Basic so the LED is on when the relay is off?~

    LedState default value is 1 (on) - Show power state on LED. The LED can be disabled completely with LedState 0 (off). However, there is no option to invert the output of the green LED on the Sonoff Basic.

    Flashing~

    Cannot enter flash mode~

    Be sure to press the button correctly, you must "feel" a click. If your on-device button doesn't allow you to enter flash mode or there is no GPIO0 broken out to the PCB, you can always bridge GND to GPIO0 pin directly on the chip. Search on the Internet for your chip's pinouts and use the tutorial. Be sure to keep GPIO0 grounded long enough (3-5 seconds) before disconnecting to ensure the chip has booted completely into programming mode. On devices that do not provide a GPIO0 connected button, it may be easier to leave the wired bridge in place throughout the entire flashing process (erase & upload). Doing so will not create any problems for flashing the device. After the firmware is uploaded successfully, remove the bridge. This allows the device to boot normally.

    Flashing issues~

    • Double check if you wired the device the serial-to-USB adapter correctly. Almost every device needs RX and TX pins switched to TX and RX. See Hardware Preparation for more.

    • Another common problem are the jumper cables used. Try another cable if you keep getting connection errors or check the cables for connectivity. Most of them are made cheaply and it happens quite often that those cables do not offer a good connection because of bad crimping or broken copper lines in them.

    • Be sure to use a USB Data Cable and not a cheap loading cable for mobile phones for connecting the serial-to-USB adapter to your computer. If you are unsure, just try another USB cable. Data USB cables are often thicker than the normal loading cables (and more expensive).

    • Another problem can be the difficulties in getting the ESP chip into programming mode when it boots.

    • If the flash still fails or the progress interrupts, it could be that your computer or serial-to-USB adapter doesn't provide enough power to the device. Try another computer or use an external power supply (3.3V one). More infos about insufficient power

    • Use the correct serial-to-USB adapter driver. Check the model of your adapter chip and get the correct driver.

    • If the flash completes successfully, but you get a hash mismatch (esptool.py error message A fatal error occurred: MD5 of file does not match data in flash!) ensure that your 3.3v current is sufficient. Workarounds include using a dedicated bread board power supply or using the 3.3v output of an additional microcontroller. If using an additional power supply to power the device, be sure to use a common ground for the power supply, the device to be flashed and the serial-to-USB adapter.

    • If esptool.py stops at "Uploading stub...", use --no-stub

    • If the flash fails or the device does not operate as expected, try using the default ESP82xx boot ROM baud rate - 74880. This is the baud rate the ESP82xx is set to by default when it boots into programming mode. It can be specified as a command line option in esptool.py (-b) and esptool.exe (-cb).

    You may also want to select a serial monitor/terminal capable of setting this "unusual" baud rate. In Termite, type this value (74880) in the baud rate selection text box when configuring the port. Having the option to specify this unusual baud rate will allow you to view the ESP8266 boot ROM log while the device is booting.

    Device is hot to the touch~

    Remember - NEVER EVER FLASH WITH 5V!?

    Better unpower your device and check if the wiring is correct and the voltage is on your FTDI is set to 3.3V. If you've connected VCC to the wrong pin it might cause your device to overheat and destroy it.

    There was white smoke and the device does not work anymore!~

    Yes, you've released the fabled "white smoke", the mysterious substance all electronic devices work on.

    In the immortal words of Doctor Bones: It's dead Jim!

    Sonoff 4CH V2 / Sonoff Dual V2 will not flash~

    Testing with two different (fairly new) FTDI boards and two Sonoff 4CH v2.0 and the Sonoff Dual v2.0 boards I found that I was getting errors uploading sketches i.e. "warning: espcomm_sync failed" basically a lack of communication between the two devices.

    I found that the problem in both Sonoff's was that instead of the FTDI Sonoff cross-over TX->RX and RX->TX I had to do TX->TX RX->RX this then allowed me to upload the sketch.

    Flashing fails on MacOS High Sierra~

    Related to issue #957.

    Solution:

    1. Install the VCP drivers for Mac from the FTDI website
    2. After install, reboot (it does not work if you do not reboot).
    3. After reboot, plug the FTDI USB/serial converter. Accept the security alert from MacOS.
    4. Restart the flash process. It works!

    Miscellaneous~

    Can you add this unsupported sensor to Tasmota~

    Short answer: NO!

    Long answer: There is not enough time in our coders lives to take requests, if you can code a driver for that sensor and submit a PR it will be considered, otherwise you can only wait for someone else to do it.

    Available Characters for HOSTNAME~

    • 24 chars max
    • only a..z A..Z 0..9 '-'
    • no '-' as last char

    Notice: Hostnames containing % will reset to MQTT_TOPIC-<4digits>. It is not intended to use internal Tasmota variables here.

    RFC952
    ASSUMPTIONS

    1. A "name" (Net, Host, Gateway, or Domain name) is a text string up to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus sign (-), and period (.). Note that periods are only allowed when they serve to delimit components of "domain style names". (See RFC-921, "Domain Name System Implementation Schedule", for background). No blank or space characters are permitted as part of a name. No distinction is made between upper and lower case. The first character must be an alpha character. The last character must not be a minus sign or period. A host which serves as a GATEWAY should have "-GATEWAY" or "-GW" as part of its name. Hosts which do not serve as Internet gateways should not use "-GATEWAY" and "-GW" as part of their names. A host which is a TAC should have "-TAC" as the last part of its host name, if it is a DoD host. Single character names or nicknames are not allowed.

    Flash Memory Considerations~

    • To stop saving parameter changes to Flash or Spiffs use command SaveData off.

    • To stop saving power changes only to Flash or Spiffs use command SetOption0 off. This will disable the relay from returning to the same state after power on UNLESS you use the MQTT retain flag in which case the MQTT broker will send the last known MQTT state on restart or power on. The command ButtonRetain on will configure the button to send a MQTT command with Topic and the MQTT retain flag set.

    What is an Arduino Core~

    Arduino Core (open source) are the core libraries for ESP8266/ESP8285 chips to make them Arduino Framework Compatible. This Core is programmed on top of the Espressif SDK (closed source). Tasmota is only using the core and does not maintain it or can help in solving issues with it.

    You can see the Arduino Core Version and the Espressif SDK Version on the Tasmota WebUI under the Information Menu entry.

    I Cannot Find An Answer Here!~

    Check the Troubleshooting section or join Discord, Matrix, Telegram, Reddit or Google Groups for assistance from other Tasmota users.

    \ No newline at end of file diff --git a/Features/index.html b/Features/index.html new file mode 100644 index 0000000000..c9fbce11f6 --- /dev/null +++ b/Features/index.html @@ -0,0 +1 @@ + Introduction - Tasmota

    Introduction

    Tasmota contains myriad features and supported peripherals (sensors, controllers and similar). Due to the flash size and memory constraints of ESP not all features can be included in precompiled release binaries.

    To enable some of the features you have to compile your own binary. Features with such requirement have a warning with instructions on how to enable them.

    \ No newline at end of file diff --git a/Firmware-Builds/index.html b/Firmware-Builds/index.html new file mode 100644 index 0000000000..8fe9c8ecda --- /dev/null +++ b/Firmware-Builds/index.html @@ -0,0 +1 @@ + Firmware Builds - Tasmota
    Skip to content

    Firmware Builds

    There are many available features programmed into Tasmota. Not all devices need all of the available features. ESP based devices have a limited amount of memory available. To ensure that there is enough memory available to flash the firmware, it is best to keep the total size as small as possible, and definitely under 625K total to ensure OTA updates are possible. For this reason, Tasmota makes available the ability to create different firmware binary files to suit each particular device's requirements (e.g., sensors) and each application's needs (e.g., Rules, Timers, etc.). Once features such as support for sensors, rules, timers, etc. is compiled into the firmware binary, the features themselves can be configured at run-time, or, for some features, configured at compile time as well.

    Many times one just needs to download a pre-compiled binary and perform the necessary run-time configuration. It is not necessary to compile your own binary if these pre-compiled builds meet your needs. These available files provide a simpler approach to get up and going with Tasmota quickly.

    Release binaries are from the official OTA server. Firmware built from development branch code is available from the development OTA server.

    Features that are not available in any official release build have to be enabled in source code and compiled yourself. Read more about compiling your own build.

    Tip

    You might find some of the features you need included in one of our unofficial experimental builds over at https://github.com/tasmota/install.

    Firmware Variants~

    • tasmota.bin supports most features. THIS IS THE RECOMMENDED BINARY
      tasmota-BG.bin to tasmota-TW.bin the same features as tasmota.bin with localized language support. (Note: tasmota-UK.bin is for the Ukrainian language)
    • tasmota-sensors.bin enables many features as tasmota.bin - not all - and includes support for connectable sensors
    • tasmota-lite.bin has a stripped down feature set. This reduces the required CPU cycles in order to reduce power draw to allow devices with a weak power supply to run reliably.

    Note

    This is similar to the pre-compiled Tasmota binary that comes bundled with Tuya-Convert. If you used Tuya-Convert to flash your device, it is strongly recommended to update to tasmota.bin. Otherwise some crucial features (e.g., energy monitoring, auto-discovery, etc.) will not work.

    • tasmota-minimal.bin is a specialised build to subsequently allow OTA uploads. This version should NOT be used for initial installation!
    • tasmota-knx.bin includes KNX support but omits some features. If you need additional features, compile your own firmware (e.g., Gitpod ).
    • tasmota-display.bin built for connecting displays but omits some features such as energy monitoring. If you need additional features, compile your own firmware (e.g., Gitpod ).
    • tasmota-ir.bin provides almost all IRremoteESP8266 protocols. If you need additional features, compile your own firmware (e.g., Gitpod) to compile ircustom.
    • tasmota-zbbridge.bin built specifically for Sonoff Zigbee Bridge device.

    Available Features and Sensors for Each Build~

    \ No newline at end of file diff --git a/For-Developers/index.html b/For-Developers/index.html new file mode 100644 index 0000000000..593f1dbec1 --- /dev/null +++ b/For-Developers/index.html @@ -0,0 +1 @@ + For Developers - Tasmota
    Skip to content
    \ No newline at end of file diff --git a/GPIO-Conversion/index.html b/GPIO-Conversion/index.html new file mode 100644 index 0000000000..8af591461f --- /dev/null +++ b/GPIO-Conversion/index.html @@ -0,0 +1 @@ + GPIO Conversion - Tasmota
    Skip to content

    GPIO Conversion

    GPIO functions define the action being taken by a device connected to it's pin. The current GPIO function configuration supports 216 GPIO functions like Relay1 (21) or Inverted Button1 (122).

    The current implementation supports up to 250 functions as it is confined to 8-bits uint8_t arrays.

    As more devices need to be connected it would become impossible to have them addressed with this 8-bit scheme. Another problem is that the current implementation would need GUI drop down boxes with up to 250 items which is hard to use. Also user templates still need to be able to address all possible GPIO functions.

    To solve this issue Theo needed to change from 8-bit uint8_t to 16-bit uint16_t arrays. Without further action this would result in even more entries in the GUI drop down boxes and made adding more relays, buttons, switches, etc also very cumbersome. As the ESP32 would need to be supported too a challenge lay ahead.

    Theo decided for the following option where every GPIO function type (relays, buttons, switches) would have an id allowing for 32 devices. So every GPIO function would be offset by 32 (or 5-bits). These 5-bits can be used in the future to easily expand current max values like 4 buttons or 8 relays to max 32. How this works could have been seen in the ESP32 implementation. The GUI now contains two drop down boxes, one for the GPIO function (Relay) and one for the function index (1 to 8).

    For the ESP8266 the current maxed GPIO functions of 216 will be fixed an any new device will be using the GPIO addressing scheme. To make the transition transparent, a table is used to convert current 216 GPIO 8-bit function numbers to new 16-bit function numbers. As both are offset from 0 there will be some duplication in numbering so the only way to distinguish them by external programs like TDM is by checking the Tasmota version number: below 9.0 will be 8-bit GPIO and starting with development version 9.0.0.1 it will be 16-bit GPIO.

    See below how the numbers are being converted. The first number is 8-bit GPIO, the second the 16-bit GPIO.

    GPIO Conversion~

    Old GPIO New GPIO Name Description
    255 1 User User
    0 0 None Not used
    1 1184 DHT11 DHT11 sensor
    2 1216 AM2301 AM230X, DHT21 and DHT22 sensor
    3 1248 SI7021 Only for Sonoff Si7021, not the i2c version
    4 1312 DS18x20 Dallas Semiconductor DS18b20 1-Wire temperature sensor
    5 608 I2C SCL I2C serial clock pin, used with any I2C component (sensors, displays, ...)
    6 640 I2C SDA I2C serial data pin, used with any I2C component (sensors, displays, ...)
    7 1376 WS2812 Addressable LEDs such as WS281X or Neopixel
    8 1056 IRsend IR Transmitter LED
    9 160 Switch1 Switch
    10 161 Switch2 Switch
    11 162 Switch3 Switch
    12 163 Switch4 Switch
    13 164 Switch5 Switch
    14 165 Switch6 Switch
    15 166 Switch7 Switch
    16 167 Switch8 Switch
    17 32 Button1 Button
    18 33 Button2 Button
    19 34 Button3 Button
    20 35 Button4 Button
    21 224 Relay1 Relay
    22 225 Relay2 Relay
    23 226 Relay3 Relay
    24 227 Relay4 Relay
    25 228 Relay5 Relay
    26 229 Relay6 Relay
    27 230 Relay7 Relay
    28 231 Relay8 Relay
    29 256 Relay1i Relay inverted
    30 257 Relay2i Relay inverted
    31 258 Relay3i Relay inverted
    32 259 Relay4i Relay inverted
    33 260 Relay5i Relay inverted
    34 261 Relay6i Relay inverted
    35 262 Relay7i Relay inverted
    36 263 Relay8i Relay inverted
    37 416 PWM1 Pulse Width Modulated Output
    38 417 PWM2 Pulse Width Modulated Output
    39 418 PWM3 Pulse Width Modulated Output
    40 419 PWM4 Pulse Width Modulated Output
    41 420 PWM5 Pulse Width Modulated Output
    42 352 Counter1 Counter Input
    43 353 Counter2 Counter Input
    44 354 Counter3 Counter Input
    45 355 Counter4 Counter Input
    46 448 PWM1i Pulse Width Modulated inverted Output
    47 449 PWM2i Pulse Width Modulated inverted Output
    48 450 PWM3i Pulse Width Modulated inverted Output
    49 451 PWM4i Pulse Width Modulated inverted Output
    50 452 PWM5i Pulse Width Modulated inverted Output
    51 1088 IRrecv IR Receiver Input (for example TSOP1838)
    52 288 Led1 LED
    53 289 Led2 LED
    54 290 Led3 LED
    55 291 Led4 LED
    56 320 Led1i Inverted LED - default state ON
    57 321 Led2i Inverted LED - default state ON
    58 322 Led3i Inverted LED - default state ON
    59 323 Led4i Inverted LED - default state ON
    60 1408 MHZ Rx MHZ 19 CO2 Sensor
    61 1440 MHZ Tx MHZ 19 CO2 Sensor
    62 1472 PZEM0XX Tx Peacefair Pzem-0XX Power Meter Tx pin
    63 1504 PZEM004 Rx Peacefair Pzem-004 Power Meter Rx pin
    64 1600 SAir Rx Sensor Senseair
    65 1632 SAir Tx Sensor Senseair
    66 768 SPI CS SPI Interface (ePaper Display)
    67 800 SPI DC SPI Interface (ePaper Display)
    68 992 BkLight Backlight (Display)
    69 1696 PMS5003 PMS5003 Air Quality Sensor
    70 1760 SDS0X1 Rx Nova Fitness SDS011 Laser Dust Sensor Rx pin
    71 1792 SerBr Rx Serial Bridge Receive
    72 1824 SerBr Tx Serial Bridge Transmit
    73 1856 SR04 Tri Ultrasonic Sensor HC-SR04 Trigger pin
    74 1888 SR04 Ech Ultrasonic Sensor HC-SR04 Echo pin
    75 1920 SDMx20 Tx SDMx20-Modbus Multifunction Power Analyser Tx pin
    76 1952 SDMx20 Rx SDMx20-Modbus Multifunction Power Analyser Rx pin
    77 1984 SDM630 Tx SDM630-Modbus Multifunction Power Analyser Tx pin
    78 2016 SDM630 Rx SDM630-Modbus Multifunction Power Analyser Rx pin
    79 2048 TM16 CLK TM1638 Switch Module
    80 2080 TM16 DIO TM1638 Switch Module
    81 2112 TM16 STB TM1638 Switch Module
    82 192 Switch1n Switch, no pullup resistor
    83 193 Switch2n Switch, no pullup resistor
    84 194 Switch3n Switch, no pullup resistor
    85 195 Switch4n Switch, no pullup resistor
    86 196 Switch5n Switch, no pullup resistor
    87 197 Switch6n Switch, no pullup resistor
    88 198 Switch7n Switch, no pullup resistor
    89 199 Switch8n Switch, no pullup resistor
    90 64 Button1n Button, no pullup resistor
    91 65 Button2n Button, no pullup resistor
    92 66 Button3n Button, no pullup resistor
    93 67 Button4n Button, no pullup resistor
    94 384 Counter1n Counter sensor, no pullup resistor
    95 385 Counter2n Counter sensor, no pullup resistor
    96 386 Counter3n Counter sensor, no pullup resistor
    97 387 Counter4n Counter sensor, no pullup resistor
    98 1536 PZEM016 Rx Peacefair Pzem-016 Power Meter Rx pin
    99 1568 PZEM017 Rx Peacefair Pzem-017 Power Meter Rx pin
    100 2144 MP3 Player DF MP3 Player mini (Input)
    101 1728 SDS0X1 Tx Nova Fitness SDS011 Laser Dust Sensor Tx pin
    102 2176 HX711 SCK HX711 weight sensor serial clock input
    103 2208 HX711 DAT HX711 weight sensor data output
    104 2240 TX20 TX20 Wind Sensor Input (Tx from sensor)
    105 1120 RFSend RF Emitter (433Mhz module needed; Requires self-compile with RF_SENSOR and USE_RC_SWITCH)
    106 1152 RFrecv RF Receiver (433Mhz module needed; Requires self-compile with RF_SENSOR and USE_RC_SWITCH)
    107 2272 Tuya Tx Tuya Transfer pin
    108 2304 Tuya Rx Tuya Receive pin
    109 2336 MGC3130 Xfr MGC3130 E-field Xfr pin
    110 2368 MGC3130 Rst MGC3130 E-field Reset pin
    111 832 SSPI MISO Software SPI MISO (Display)
    112 864 SSPI MOSI Software SPI MOSI (Display)
    113 896 SSPI SCLK Software SPI SCLK (Display)
    114 928 SSPI CS Software SPI CS (Display)
    115 960 SSPI DC Software SPI DC (Display)
    116 2400 RF Sensor Theo Arendst RF433 Sensor
    117 2432 AZ Rx AZ 7798 CO2 datalogger
    118 2464 AZ Tx AZ 7798 CO2 datalogger
    119 2496 MX31855 CS MAX31855 Thermocouple Sensor Chip Select pin
    120 2528 MX31855 CLK MAX31855 Thermocouple Sensor Serial Clock pin
    121 2560 MX31855 DO MAX31855 Thermocouple Sensor Digital Output pin
    122 96 Button1i Button inverted
    123 97 Button2i Button inverted
    124 98 Button3i Button inverted
    125 99 Button4i Button inverted
    126 128 Button1in Button inverted, no pullup resistor
    127 129 Button2in Button inverted, no pullup resistor
    128 130 Button3in Button inverted, no pullup resistor
    129 131 Button4in Button inverted, no pullup resistor
    130 2592 HLWBL SEL Energy Monitoring (for example Pow)
    131 2624 HLWBL SELi Energy Monitoring (for example Pow)
    132 2656 HLWBL CF1 Energy Monitoring (for example Pow)
    133 2688 HLW8012 CF HLW8012 Single Phase Energy Monitor Chip CF pin
    134 2720 BL0937 CF BL0937 Single Phase Energy Monitor Chip CF pin
    135 2752 MCP39F5 Tx Energy Monitoring (for example Shelly2)
    136 2784 MCP39F5 Rx Energy Monitoring (for example Shelly2)
    137 2816 MCP39F5 Rst Energy Monitoring (for example Shelly2)
    138 2848 PN532 Tx PN532 RFID/NFC Reader Tx pin
    139 2880 PN532 Rx PN532 RFID/NFC Reader Rx pin
    140 2912 SM16716 CLK SM16716 Pixel LED Serial Clock pin
    141 2944 SM16716 DAT SM16716 Pixel LED Data pin
    142 2976 SM16716 PWR SM16716 Pixel LED Power pin
    143 3008 MY92x1 DI Light Bulb with MY92x controller
    144 3040 MY92x1 DCKI Light Bulb with MY92x controller
    145 3072 CSE7766 Tx CSE7766 Single Phase Energy Monitor Chip Tx pin
    146 3104 CSE7766 Rx CSE7766 Single Phase Energy Monitor Chip Rx pin
    147 3136 ALux IrRcv AriLux RGB Controller IR receive (Input)
    148 3200 Serial Tx Serial Transfer pin
    149 3232 Serial Rx Serial Receive pin
    150 3264 Rotary1a Rotary Encoder (Mi Desk Lamp)
    151 3296 Rotary1b Rotary Encoder (Mi Desk Lamp)
    152 3265 Rotary2a Rotary Encoder (Mi Desk Lamp)
    153 3297 Rotary2b Rotary Encoder (Mi Desk Lamp)
    154 3392 HRE CLOCK Clock/Power line for HR-E Water Meter
    155 3424 HRE DATA Data line for HR-E Water Meter
    156 3456 ADE7953_IRQ ADE7953 IRQ
    157 544 LedLink Device Status LED
    158 576 LedLinki Device Status LED, inverted
    159 3168 ALux IrSel For AriLux devices - switches between IR/RF mode
    160 480 Buzzer Sonoff iFan03 Buzzer
    161 512 Buzzeri Sonoff iFan03 Buzzer inverted
    162 1024 OLED Reset OLED Display Reset
    163 3488 SolaxX1 Tx Solax Inverter Tx pin
    164 3520 SolaxX1 Rx Solax Inverter Rx pin
    165 3552 Zigbee Tx Zigbee Serial interface Tx
    166 3584 Zigbee Rx Zigbee Serial interface Rx
    167 3616 RDM6300 Rx RDM6300 RX
    168 3648 iBeacon Tx HM17 iBeacon Tx
    169 3680 iBeacon Rx HM17 iBeacon Rx
    170 3712 A4988 DIR A4988 Motor Direction
    171 3744 A4988 STP A4988 Step motor
    172 3776 A4988 ENA A4988 Enable motor
    173 3808 A4988 MS1 A4988 Microstep increment select pin1
    174 3809 A4988 MS2 A4988 Microstep increment select pin2
    175 3810 A4988 MS3 A4988 Microstep increment select pin3
    176 3904 DDS238-2 Tx DDS2382 Serial interface Tx
    177 3936 DDS238-2 Rx DDS2382 Serial interface Rx
    178 3968 DDSU666 Tx DDSU666 Serial interface Tx
    179 4000 DDSU666 Rx DDSU666 Serial interface Rx
    180 4032 SM2135 CLK SM2135 Clk
    181 4064 SM2135 DAT SM2135 Dat
    182 4096 DeepSleep DeepSleep wake switch
    183 4128 EXS Enable EXS Dimmer MCU Enable
    184 4160 Slave TX TasmotaClient TX
    185 4192 Slave RX TasmotaClient RX
    186 4224 Slave RST TasmotaClient Reset Pin
    187 4256 Slave RSTi TasmotaClient Reset Inverted
    188 4288 HPMA RX Honeywell HPMA115S0 Serial Rx
    189 4320 HPMA TX Honeywell HPMA115S0 Serial Tx
    190 4352 GPS RX GPS Serial Tx
    191 4384 GPS TX GPS Serial Tx
    192 1344 DSB OUT Pseudo Single wire DS18B20 or DS18S20
    193 1280 DHT11 OUT Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321
    194 4416 HM10 RX HM10-BLE-Mijia-bridge Serial
    195 4448 HM10 TX HM10-BLE-Mijia-bridge Serial
    196 4480 LE01MR RX F&F LE-01MR Energy Meter Serial
    197 4512 LE01MR TX F&F LE-01MR Energy Meter Serial
    198 4544 CC1101 GDO0 CC1101 RX Pin
    199 4576 CC1101 GDO2 CC1101 TX Pin
    200 4608 HRXL RX Data from MaxBotix HRXL sonar range sensor
    201 4640 MOODL TX ElectriQ iQ-wifiMOODL Serial TX
    202 4672 AS3935 Lightning Detector Interrupt Pin
    203 1664 PMS5003 TX Plantower PMS5003 Serial interface
    204 4928 Boiler OT Rx OpenTherm Boiler RX pin
    205 4960 Boiler OT Tx OpenTherm Boiler TX pin
    206 4992 Windmeter Speed WindMeter speed counter pin
    207 5056 BL0940 RX BL0940 serial interface
    208 5088 TCP TX TCP Serial bridge
    209 5120 TCP RX TCP Serial bridge
    210 5152 TELEINFO RX Teleinfo serial interface
    211 5184 TELEINFO Enable Teleinfo Enable PIN
    212 5216 LMT01 LMT01 input counting pin
    213 5248 IEM3000 TX IEM3000 Serial interface
    214 5280 IEM3000 RX IEM3000 Serial interface
    215 5312 Zigbee RST Zigbee reset
    216 5344 DYP Rx DYP-ME007 Rx pin

    ADC Conversion~

    Old ADC New ADC Option WebUI display MQTT message
    0 0 None none none
    1 4704 Analog Analog0 %value% {"A0":%value%}
    2 4736 Temperature Temperature %value% °C (°F) {"Temperature":%value%},"TempUnit":"C"}
    3 4768 Light Illuminance %value% lux {"Illuminance":%value%}
    4 4800 Button none none
    5 4832 Buttoni none none
    6 4864 Range Range %value% {"Range":%value%}
    7 4896 CT Power Voltage 230 V Current %value A Power %value W Energy Total %value kWh {"Energy":%value,"Power":%value,"Voltage":230,"Current":%value}
    8 3328 Joystick none {"ANALOG":{"Joy1":%value%}
    \ No newline at end of file diff --git a/GPIO-Locations/index.html b/GPIO-Locations/index.html new file mode 100644 index 0000000000..eee54435bb --- /dev/null +++ b/GPIO-Locations/index.html @@ -0,0 +1,11 @@ + + + + + + + + +Redirecting... + + diff --git a/GPS-NTP-server/index.html b/GPS-NTP-server/index.html new file mode 100644 index 0000000000..2b19b6fcfb --- /dev/null +++ b/GPS-NTP-server/index.html @@ -0,0 +1,5 @@ + GPS-based NTP-server (Serial) - Tasmota
    Skip to content

    GPS-based NTP-server (Serial)~

    This feature is not included in precompiled binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_GPS
    +#define USE_GPS                                  // Add support for GPS and NTP Server for becoming Stratus 1 Time Source (+3k1 code, +132 bytes RAM)
    +  #define USE_FLOG                               // Add support for GPS logging in OTA's Flash (Experimental) (+2k9 code, +8 bytes RAM)
    +#endif
    +

    The foremost reason for the inclusion of this driver was to have a network unrelated time source, but besides this location data is provided too.

    Only u-blox-devices are supported

    The UBX binary protocol was chosen, because it is very memory efficient and all the needed data can be read without further parsing. The generic NMEA-standard is NOT supported!

    The driver is tested on a NEO-6m and a Beitian-220. Series 7 should work too. These modules are quite cheap, starting at about 3.50€ for the NEO-6m.

    Features:~

    • simplified NTP-Server
    • sets system time automatically and Settings.latitude and Settings.longitude via command
    • get position and time data
    • can log position data with timestamp to flash with a small memory footprint of only 12 Bytes per record (!!EXPERIMENTAL!!)
    • constructs a GPX-file for download of this data
    • Web-UI and console command interface

    Connecting the GPS-device to a Wemos D1 mini~

    Wemos D1 Mini GPS
    VCC +3.3V Vin
    GND GND
    TX Rx
    RX Tx

    Tasmota Settings~

    In the Configuration -> Configure Module page, select the following for Wemos D1 mini: 1. Module Type: 18 Generic 2. TX GPIO1 Serial Out: GPX_TX 3. RX GPIO3 Serial In: GPX_RX

    No further installation steps needed. To get more debug information compile it with option "DEBUG_TASMOTA_SENSOR". The driver expects the device to be configured for 9600 baud, which is the default for most of these sensors. If necessary the configuration can be changed with the freely available "u-center"-software, which is windows-only but is reported to run quite well with WINE and can be confirmed to work with CrossOver on macOs Catalina.

    NTP-Server~

    Simply start the server with 'sensor60 9'.

    Flash-Log~

    This is highly experimental feature, which uses the OTA-partition to log position data.

    !! ⚠️ FOR OBVIOUS REASONS THIS WILL SHORTEN THE LIFE OF THE FLASH MEMORY AND SHOULD BE USED WISELY ⚠️ !!

    After the first recording, a download-link will appear in the web interface. By clicking on it, a GPX-file will be created on-the fly for download. All recorded data is lost after each OTA-update.

    Commands~

    Command Description
    Sensor60 0 write to all available sectors, then restart and overwrite the older ones
    Sensor60 1 write to all available sectors, then restart and overwrite the older ones
    Sensor60 2 filter out horizontal drift noise
    Sensor60 3 turn off noise filter
    Sensor60 4 start recording, new data will be appended
    Sensor60 5 start new recording, old data will lost
    Sensor60 6 stop recording, download link will be visible in Web-UI
    Sensor60 7 send mqtt on new position TELE -> consider to set TELE to a very high value
    Sensor60 8 only TELE message
    Sensor60 9 start NTP-Server
    Sensor60 10 deactivate NTP-Server
    Sensor60 11 force update of Tasmota-system-UTC with every new GPS-time-message
    Sensor60 12 do NOT update of Tasmota-system-UTC with every new GPS-time-message
    Sensor60 13 set latitude and longitude in settings
    Sensor60 14 open virtual serial port over TCP, usable for u-center
    tcp://ip-address:port (default port 1234)
    ⚠️ misconfiguration via u-center can virtually brick the device ⚠️
    Sensor60 15 pause virtual serial port over TCP (connection stays active in the background)
    \ No newline at end of file diff --git a/Getting-Started/index.html b/Getting-Started/index.html new file mode 100644 index 0000000000..3171bbd2a3 --- /dev/null +++ b/Getting-Started/index.html @@ -0,0 +1,8 @@ + Getting Started - Tasmota
    Skip to content

    Getting Started

    Prerequisites~

    Needed Hardware~

    ESP Device~

    Every Espressif ESP8266, ESP8285, ESP32, ESP32-S or ESP32-C3 chipset based device can be flashed with Tasmota. The term ESP refers to any of them.

    Serial Programmer~

    The power supplied to the device is one of the most important elements for both flashing the device and for stable operation. You must ensure that the device receives sufficient power (current AND appropriate voltage level) to properly flash the firmware on the device.

    • RECOMMENDED CH340G is the most reliable and the cheapest one to boot (CH340G, Sparkfun, Soldered Connect, CH340N with AMS1117).
    • RECOMMENDED VoltLink - USB to serial adapter board based on the popular CP2102N chip with built-in ESP auto-reset circuitry and a 500mA voltage regulator
    • CP2102 or PL2303 - works with certain devices, but using an external 3.3V supply might be necessary. Not recommended for beginners!
    • NodeMCU You can also use a NodeMCU (or similar) as a reliable serial programmer if you disable the onboard ESP by bridging GND to the RST or EN pin, and connect TX and RX straight to another ESP82xx instead of crossed.

    Don't forget to install drivers for your serial programmer.

    Danger

    Some adapters can be switched between 3.3V and 5V for the data pins, but still provide 5V on the power pin which will irreparably destroy your device. You MUST make sure the data (RX and TX) and VCC pins are set for 3.3V.

    Many serial programmers do not have a voltage regulator on board like the pictured ones. The ESP requires at least 150mA, many 3.3V serial programmers cannot supply this much current as many serial programming tasks do not require a large amount of power.

    When using an external 3.3V supply, ensure the ground (GND) of both are connected together, this ensures a common ground. A PC power supply can be a source for 3.3V DC power.

    Devices with an USB upload port typically have a serial programmer built in, such as NodeMCU, D1 mini or M5Stack products.

    Soldering Tools~

    To solder you'll of course need a soldering iron, soldering tin and some flux. If you're new to soldering check out some soldering tutorial videos while you're at it.

    If you're intimidated by soldering there are 3D printed jigs available for different modules and devices. At worst, you could get away with holding the headers tightly with jumper wires in pin holes during flashing but it is not a foolproof process and flashing might fail.

    Jumper Wires~

    You could use any kind of wire but jumper wires (also called DuPont wires) are more practical than soldering and desoldering.

    Pin Headers~

    Pin headers come in male or female version. Choose according to your jumper wire connectors.

    Computer with Linux, Windows or MacOS~

    You need a computer with a USB port to upload the firmware to your device and configure it.

    Smartphone~

    Tasmota installed from a precompiled binary needs to be configured to work with your Wi-Fi network before you can access the Tasmota web UI. This is usually done by connecting to a Tasmota Wi-Fi Access Point with your smartphone (or tablet or computer with Wi-Fi).

    Needed Software~

    Tasmota Firmware Binary~

    Download a Tasmota firmware binary file (.bin). If you're not sure which binary is the right one for you just start with tasmota.bin or consult the builds table to see which features you need.

    Official release binaries can be downloaded from firmware server.

    Latest development branch binaries are available only from our OTA server. The latest merged development code is compiled hourly.

    Flashing Tool~

    • Tasmota Web Installer - flash Tasmota using a Chrome based browser for ESP82XX and ESP32
    • Tasmotizer - flashing and firmware download tool for ESP82XX only. (Windows, Linux or Mac)
    • ESP-Flasher - GUI flasher for Tasmota based on esptool.py for ESP82XX and ESP32. (Windows, Linux or Mac)
    • Esptool.py - the official flashing tool from Espressif for ESP82XX and ESP32. (Requires Python)
    Compiling Tools (optional)

    If you want to modify the code or default settings and compile your own Tasmota firmware.

    MQTT Knowledge~

    Tasmota is designed to be controlled and communicate via MQTT. To use it to its fullest potential you need an MQTT broker.

    Read our article on MQTT to learn why it is essential in Tasmota.

    Hardware Preparation~

    We need to connect to the serial programming interface of the ESP chip. This is done by connecting our serial-to-USB converter TX and RX pins to the ESP RX and TX pins and powering the chip with the 3.3V and GND pins.

    In most cases those pins are available on the PCB in the form of pin holes or solder pads but pin headers or jumper wires need to be soldered or otherwise applied. In some cases you will need to solder wires directly on the chip's pins which requires some experience and good soldering equipment.

    DO NOT CONNECT DEVICES TO MAINS AC POWER WHILE THE COVER IS OPEN AND CIRCUIT BOARD IS EXPOSED!!!

    Sonoff Pow Bricked

    NEVER TRY TO FLASH WHILE YOUR DEVICE IS CONNECTED TO MAINS POWER!!!

    YOU CAN BE ELECTROCUTED IF YOU DO NOT KNOW WHAT YOU ARE DOING!

    If you are not careful, your own health will be in danger. Shorting your serial interface with mains AC power will fry your device and serial adapter and will also harm or destroy your computer. It is important to always have all mains power cables disconnected from the device while being connected via serial or even while the case of the device is opened.

    Serial Connection~

    Each device has its pins labelled differently. If the labelling isn't visible on the PCB please refer to the devices flashing guide or search the Internet for correct pin locations. Device specific instructions and restrictions are documented in the Tasmota Supported Devices Repository. Pinouts for commonly used Wi-Fi modules are found here

    When you have identified pins on your device, connect wires according to the table:

    Serial adapter ESP device
    3V3 3V3 or VCC
    TX RX
    RX TX
    GND GND

    Note that TX from your adapter goes to RX on the ESP device and RX from adapter goes to TX on the device! Image courtesy of https://www.domo-blog.fr/

    Programming Mode~

    Typical GPIO0 Location

    ESP needs to be put into programming mode or flash mode before the firmware can be uploaded. This is done by connecting GPIO0 pin to GND while the chip is booting.

    On many devices the installed control button is connected to GPIO0 and GND, making entering Programming Mode easy. On others you will need to bridge the pins on the PCB or directly on the chip with a jumper wire. GPIO0 locations for popular modules can be found in Pinouts!

    Device specific instructions are documented in Tasmota Supported Devices Repository.

    To put the ESP into Programming Mode:

    1. Disconnect serial programmer and power
    2. Bridge GPIO0 and GND (by pressing the on-board button or connection with a wire)
    3. Connect the serial programmer to your computer
    4. After a few seconds disconnect GPIO0 from GND (release button or remove the wire connection). On devices that do not provide the GPIO0 connected button, it may be easier to leave the wired bridge in place throughout the entire flashing process (erase & upload). Doing so will not create any problems. After the firmware is uploaded successfully, remove the bridge. This allows the device to boot normally.
    esptool.py programming mode test

    You can test whether your device is in Programming Mode by attempting to read information from the ESP82xx chip. This requires esptool.py. Instructions on installing and using esptool are provided below. For example (COM5 will be your COM port):

    • esptool.py -p COM5 read_mac (It should read the MAC address. It may fail afterwards during Uploading and running a "stub". This is normal.)
    • esptool.py -p COM5 flash_id

    If everything went well, you are now in Programming Mode and ready to continue with flashing. If the flashing process is unable to start, disconnect the device and retry the steps.

    Common Mistakes~

    • Wire connections and solder joints - Double check all connections and also check for solder overflow.
    • Use a USB data cable - Some USB cables are for charging only and do not connect the data lines needed to load the firmware onto the device.
    • Insufficient power delivered over the serial programmer. This leads to flashing failures or corrupted flash altogether. Supply more power with a separate 3.3V power supply or get an adapter with a better power supply. Be sure all DC voltages use the same GND line.
    • Recheck your serial programmer so to ensure that it supplies 3.3V voltage and NOT 5V. 5V will damage the ESP chip!
    • Releasing GPIO0 button/wire before booting is finished - It is safe to leave GPIO0 connected to GND during the entire programming process (erase & upload). Just be sure to remove the GPIO0 to GND bridge before booting the device for regular operation.
    • Make sure that the RX pin is connected to the TX pin between the serial adapter and your ESP device, and vice versa.
    • Erase the flash first and power cycle before uploading the Tasmota firmware binary. Not erasing can leave behind remnants of the previous flash contents which can interfere with the new firmware operation.

    Flashing~

    If you have followed Hardware preparation, your device should be in Programming Mode and ready for a Tasmota firmware binary to be installed.

    You may want to back up the device manufacturer's firmware on the one in a million chance you don't like Tasmota.

    Choose an installation method:

    Flash Tasmota directly from your web browser.

    Tasmotizer! is specifically designed for use with Tasmota with an easy to use GUI and esptool.py under the hood.

    Download the latest release for your platform. In Windows just double click the downloaded file and it'll start, no installation required. For python follow the installation instructions.

    Note

    If you get an anti-virus infection warning don't fret, it is a known false positive. If you're still apprehensive you can always run the Python version.

    It is time to Tasmotize!

    Tasmotizer UI

    1. Connect your device to the serial programmer or plug in NodeMCU/D1 mini.
    2. Check whether the correct serial port (COM or tty port) is selected. Tasmotizer! will try its best to select the right one for you.
    3. Choose Tasmota firmware binary:
      • BIN file - browse to the Tasmota firmware binary you downloaded or compiled.
      • Release - select from a list of available release binaries
      • Development - select from a list of latest development binaries
    4. optional Backup the original device firmware
    5. Erase flash

    Danger

    Leave Erase before flashing checked if it is the first time flashing Tasmota on the device or you're experiencing issues with the existing flash and need to do a full erase. If you're upgrading an already flashed Tasmota and wish to keep your settings, uncheck Erase before flashing.

    1. Click Tasmotize and wait until done.

    Tasmotizer progress

    If the flash was successful it will display:

    Tasmotizer success

    Unplug your serial programming adapter or device and plug it back in or connect to another power source.

    Esptool is the official Espressif tool for flashing ESP chips. It requires Python, if you do not have an installed copy of Python 2.x or 3.x download and install it from https://www.python.org.

    Download the esptool Source code to a folder of your choice. Go to the folder and install Esptool with command

    python setup.py install
    +

    Make sure you followed the steps to put your device in programming mode. Place your chosen firmware binary file in the same folder as esptool.py.

    Esptool uses the serial interface to communicate with your device. On Windows these interfaces are named COM1, COM2, etc. and on Linux they are named /dev/ttyUSB0, /dev/ttyUSB1, etc. Before using esptool, make sure you know which serial port your programming adapter is connected to.

    The following use COM5 as an example. Change COM5 with your serial port designation.

    Ensure the device is in programming mode before each step.

    Backup Firmware (optional step)~

    Backup the current firmware with the following command:

    esptool.py --port COM5 read_flash 0x00000 0x100000 fwbackup.bin
    +
    When the command completes the device is not in programming mode anymore. Repeat the process of putting your device in programming mode.

    Erase Flash Memory~

    Erase the complete flash memory holding the firmware with the following command:

    esptool.py --port COM5 erase_flash
    +
    It only takes a few seconds to erase 1M of flash.

    When the command completes the device is not in programming mode anymore. Repeat the process of putting your device in programming mode.

    Upload Firmware~

    Load the chosen Tasmota firmware file with the following command (e.g., tasmota.bin in this example):

    esptool.py write_flash -fm dout 0x0 tasmota.bin
    +
    or for ESP32

    esptool.py write_flash 0x0 tasmota32.factory.bin
    +

    Unplug your serial programming adapter or your device and plug it back in or connect to another power source. Your device is now ready for Initial configuration.

    For proper device initialization after the firmware upload completes, power down and power up the device.

    Tasmota is NOT a developer of these tools. For help and troubleshooting you will need to get support from those projects.

    • MgOS to Tasmota - OTA flash for Shelly devices
    • MgOS32 to Tasmota32 - OTA flash for Shelly Plus and Pro (ESP32) devices
    • Tuya-Convert - OTA flash for devices with Tuya chips. Does not work in 99% of cases
    • Sonoff DIY - OTA flash for select Sonoff devices Does not work anymore
    • esp2ino - OTA flash for select Wyze devices. Does not work anymore

    Initial Configuration~

    Scanning for networks Once the installation is successful, click on NEXT. The installer will scan for Wi-Fi networks and select the strongest one. Use the dropdown to select between networks.

    Enter password Enter the Wi-Fi password.

    Wait for Wi-Fi configuration Wait until configuration completes and click CONTINUE

    Complete From here click on VISIT DEVICE to enter the WebUI for further configuration.

    Tasmota provides a wireless access point for easy Wi-Fi configuration.

    Tasmota APConnect your device to a power source and grab your smartphone (or tablet or laptop or any other web and Wi-Fi capable device). Search for a Wi-Fi AP named tasmota_XXXXXX-#### (where XXXXXX is a string derived from the device's MAC address and #### is a number) and connect to it. In this example the Wi-Fi AP is named tasmota_3D5E26-7718.

    When it connects to the network, you may get a warning that there is no Internet connection and be prompted to connect to a different network. Do not allow the mobile device to select a different network.

    Warning

    Wi-Fi manager server is active for only 3 minutes. If you miss the window you might have to disconnect your device from power and reconnect.

    Sign in to Wi-Fi Network After you have connected to the Tasmota Wi-Fi AP, open http://192.168.4.1 in a web browser on the smartphone (or whatever device you used). Depending on the phone, it will take you to the Tasmota configuration page automatically, or you will get a prompt to sign in to Wi-Fi network or authorize. Tapping on the AP name should also open the configuration page.

    Tasmota AP At the top of the page you can select one of the discovered Wi-Fi networks or have Tasmota scan again. Enter your WiFi credentials:

    WiFi Network - your Wi-Fi network name (SSID Selecting the desired network name from the list will enter it automatically in this field. SSID's are case sensitive

    WiFi Password - password for your Wi-Fi network
    Wi-Fi password has to be under 64 characters and without special characters (e.g. asterisks) or white spaces

    Click the checkbox if you want to see the password you enter to ensure that it is correct. Click on Save to apply the settings. The device will try to connect to the network entered.

    Tasmota AP

    If it was successful, you will see this message:

    Tasmota AP

    Some phones will redirect you to the new IP immediately, on others you need to click the link to open it in a browser.

    The tasmota_XXXXXX-#### network will no longer be present. Therefore your smartphone will automatically be disconnected and should connect back to its data network.

    Failure

    Tasmota APIn case the network name or password were entered incorrectly, or it didn't manage to connect for some other reason, Tasmota will return to the "Wi-Fi parameters" screen with an error message.

    If you don't know the IP of the newly flashed device look in your router settings or find it with an IP scanner:

    Open the IP address with your web browser and you have full access to Tasmota.

    Now is the time to set up MQTT and the last remaining, but equally important, step:

    Set up your device's feature using a Template in Configuration - Configure Template or Module in Configuration - Configure Module.

    Configure Other (optional)

    Configure your device name which is displayed in webUI and used for Home Assistant discovery.

    Configure web admin password for the webUI. Default username is admin. This type of security is rudimentary since Tasmota doesn't use HTTPS, do not expose your device outside of your local network.

    If you flashed the device using serial programmer (or it is a NodeMCU/D1 mini) you can take advantage of the existing connection and configure your device over the serial connection using Commands.

    First you will need a serial terminal program that connects to Tasmota console over the serial connection you used to flash it.

    • Termite - simple terminal for windows
    • Termie - open source clone of Termite
    • Putty - popular client available on every platform
    • Minicom - one of many Linux terminals

    Tip

    Enable local echo so that you can see what is typed and transmitted to the device. Enable Append CR+LF since every request needs to end with <CR><LF>.

    In this example Termite on Windows is used.

    Download Termite and extract the .exe file, no installation necessary. Connect your serial programmer or NodeMCU/D1 mini to the computer.

    Termite setup

    Open Termite and set it to the proper COM port (Termite selects the first available port by default). Set Baud rate to 115200 and Forward to none.

    First boot

    Connect your device to the serial programmer. You should see the initial boot output in Termite. If your screen is empty type status in the bottom command bar and hit enter. If you get a return message from your device similar to the one displayed under purple status you're all set.

    To configure Tasmota you need to issue commands, some commands will reboot your device and to avoid that we will use the Backlog command feature.

    Configure your Wi-Fi network and a secondary Wi-Fi network

    Backlog ssid1 <yourssid>; password1 <your_password>; ssid2 <your_ssid2>; password2 <your_password>
    +
    After restart

    Device will restart and connect to your network. It will display your devices newly assigned IP. Direct your web browser to that IP address to access the Web UI for further configuration and control.

    Configure MQTT broker address, MQTT credentials, unique device topic and OTA url to the latest official release

    Backlog mqtthost <yourhost>; mqttuser <user>; mqttpassword <password>; topic <unique_topic>; otaurl http://ota.tasmota.com/tasmota/release/tasmota.bin
    +

    Commands and Backlog are powerful and in time you can learn to configure almost everything (NTP servers, longitude and latitude, custom device options, etc) with a few copy and paste moves.

    Tip

    Keep your personal configuration in a text file and simply copy and paste the backlog commands to a newly flashed device.

    After Configuration~

    Your device running Tasmota is now ready to be controlled.

    Check out all the Tasmota features and ways to integrate it with other platforms.

    Warning

    If you experience power fluctuations in your power grid it's best to immediately disable Power Cycle Recovery feature with command SetOption65 1 immediately or you might end up with firmware defaults on your device.

    \ No newline at end of file diff --git a/Gitpod/index.html b/Gitpod/index.html new file mode 100644 index 0000000000..0b0aeec2d8 --- /dev/null +++ b/Gitpod/index.html @@ -0,0 +1 @@ + Gitpod - Tasmota
    Skip to content

    Gitpod

    Even though Tasmota offers several pre-compiled firmware variants, sometimes the ready-to-flash binaries aren't enough. If you need to enable or disable some features, or simply change some parameters, then you need to compile a firmware binary yourself.

    If you are not experienced enough or don't want to install an IDE (Integrated Development Environment) like PlatformIO or ATOM on your computer, compiling your own firmware is a breeze using Gitpod.

    Gitpod is a web browser based online IDE. All you need to use it is to link your GitHub account (or make a new one here). Gitpod will take care of all the necessary software package dependencies for you.

    Gitpod login

    After you successfully sign in, you can start your personal project. The fastest way to load Tasmota into Gitpod is with one of the following links:

    Browser Extension
    Gitpod has a browser extension (Chrome and Firefox) which is handy to directly load a GitHub project into your personal Gitpod work-space.
    Gitpod link

    More information on the Gitpod browser extension.

    Using Gitpod~

    After Gitpod loads the project, you will be greeted by the main window. Gitpod will then automatically compile tasmota.bin and tasmota.bin.gz. Wait for the compilation to complete.

    The display consists of three panels:

    1. Explorer
    2. Editor
    3. Terminal

    Customize Firmware Features and Settings~

    Warning

    The proper method of customizing firmware compilation options is to use the user_config_override.h file. You should not change the my_user_config.h file.

    To modify the stock configuration:

    1. Select the /tasmota folder in the Explorer (1) pane
    2. Create a new file called user_config_override.h
    3. In the Editor (2) pane, add, change, or remove anything you need in your configuration file to define your own settings. Refer to the user_config_override_sample.h file as well as my_user_config.h for #define options (sample). You can find a list of Tasmota features and settings listed here. Define the features you require in your configuration file.
    4. Click 'File' on the menu bar and 'Save' your edits.

    Prepare the IDE for Compilation~

    1. Open the platformio.ini file located in the /tasmota root directory (scroll to the bottom of the file Explorer (1) pane). In this file, removing a leading semicolon ; enables a statement.
    2. In the Editor (2) pane:
    3. If you are using your own user_config_override.h, you must tell the compiler to use it. Rename platformio_override_sample.ini to platformio_override.ini.
    4. Default Tasmota Arduino Core will be compiled. If you wish to use a different Core, find the [core_active] section in the file and enable the platform and build_flags lines for the desired Core. Then click 'File' on the menu bar and 'Save' your edits.

    Compile Your Firmware~

    This action is done in the Terminal pane (3) with simple commands.

    The simplest one is platformio run -e tasmota. With this command, Gitpod will compile the Tasmota firmware with the features you selected. If you need a different variant, you can specify this option on the command line:
    platformio run -e <variant-name>

    Examples:

    • platformio run -e tasmota-sensors
    • platformio run -e tasmota-DE

    Compilation normally takes only a couple of minutes. The time it takes is directly related to how many variants you build. When done, you will find the firmware file in the folder /Tasmota/build_output/firmware/<variant-name>.bin in the Explorer (1) pane.

    Download <variant-name>.bin to your computer by right-clicking on the file and selecting 'Download'. You are now ready to flash your device.

    Watch a livestream video by digiblurDIY of compiling Tasmota using Gitpod.

    Flash Your Device~

    OTA Flash~

    If you already have Tasmota flashed on your device, you can use the File Upload OTA method to load the new firmware binary file

    1. Download tasmota-minimal.bin.gz
    2. Make a backup of the device configuration using the web UI Configuration menu option.
    3. Upload tasmota-minimal.bin.gz to your device using the web UI Firmware Upgrade selection. Choose Upgrade by file upload.
    4. After tasmota-minimal.bin.gz is successfully loaded, select Firmware Upgrade once again and upload the firmware file compiled using Gitpod.

    Step 1-3 only applies to ESP8266, where you should use the .bin.gz file for upgrades.

    Serial Flash~

    Follow the same procedure for flashing as you would any new device.

    Webserial ESPTool~

    Install your custom firmware with a web browser using Webserial ESPTool.

    Download the tasmota-xxx.factory.bin and flash it from offset 0x0.

    Video Guide~

    \ No newline at end of file diff --git a/GladysAssistant/index.html b/GladysAssistant/index.html new file mode 100644 index 0000000000..6fb18194dd --- /dev/null +++ b/GladysAssistant/index.html @@ -0,0 +1 @@ + GladysAssistant - Tasmota
    Skip to content

    GladysAssistant

    Tasmota supports Gladys Assistant MQTT for both relays and sensors. HTTP protocol incoming soon.

    Find below the procedure to configure Gladys Assistant and Tasmota.

    Prerequisites~

    The following servers should be made available:

    • You have installed Gladys Assistant (embedded MQTT broker available)
    • You have installed/access to a MQTT broker server and made contact with your Tasmota device

    Automatic Discovery~

    Gladys Assistant automatically discovers Tasmota devices (once all are connected on same MQTT broker).

    On Gladys Assistant application:

    1. Go to Integration page
    2. Look for Tasmota element, click on it
    3. Then, MQTT discover
    4. Save to add device

    Try on Gladys Assistant demonstration website.

    \ No newline at end of file diff --git a/HASPmota/index.html b/HASPmota/index.html new file mode 100644 index 0000000000..f115244770 --- /dev/null +++ b/HASPmota/index.html @@ -0,0 +1,65 @@ + HASPmota - Tasmota
    Skip to content

    HASPmota ~

    This feature is included in tasmota32-lvgl.bin

    Tasmota is happy to support openHASP compatible format (named HASPmota), which allows to describe rich graphics interfaces using simple JSON templates. HASPmota support leverages the power of LVGL and the Berry language, but doesn't need to code nor learn the LVGL API.

    This feature is heavily inspired from @fvanroie's openHASP project.

    Minimal requirements~

    Hardware: HASPmota is supported on all ESP32 variants, and requires a display configured with universal display (using display.ini or autoconf). When they are correctly configured, you should see a splash screen at startup.

    Currently PSRAM is strongly recommended to run HASPmota.

    Firmware: HASPmota is included in tasmota32-lvgl firmwares.

    Quick tour~

    You can see HASPmota in action in a couple of minutes.

    Upload tamota_demo.tapp to your file system. The easy way is to use the following command in Tasmota console:

    • UrlFetch https://raw.githubusercontent.com/arendst/Tasmota/development/tasmota/berry/haspmota/haspmota_demo.tapp
    • Restart 1 to restart
    • Enjoy

    You should see the following screen, and click on buttons to switch to the second screen:

    haspmota1

    haspmota2

    Understanding the template~

    HASPmota automatically loads the template from a file named pages.jsonl. This file contains JSON Lines, i.e. a single JSON document per line. Each line describes an element on the screen. Elements are grouped into pages.

    Page 0 contains objects that are displayed on all screens. They are typically used for headers and menus.

    The lines below define the header label (red background) and the clock and wifi widgets.

    {"page":0,"comment":"---------- Upper stat line ----------"}
    +
    +{"id":11,"obj":"label","x":0,"y":0,"w":320,"pad_right":90,"h":22,"bg_color":"#D00000","bg_opa":255,"radius":0,"border_side":0,"text":"Tasmota","text_font":"montserrat-20"}
    +
    +{"id":15,"obj":"lv_wifi_arcs","x":291,"y":0,"w":29,"h":22,"radius":0,"border_side":0,"bg_color":"#000000","line_color":"#FFFFFF"}
    +{"id":16,"obj":"lv_clock","x":232,"y":3,"w":55,"h":16,"radius":0,"border_side":0}
    +

    The lines below describe the 3 buttons at the bottom, and their respective actions.

    {"comment":"---------- Bottom buttons - prev/home/next ----------"}
    +{"id":101,"obj":"btn","x":20,"y":210,"w":80,"h":25,"action":"prev","bg_color":"#1fa3ec","radius":10,"border_side":1,"text":"\uF053","text_font":"montserrat-20"}
    +{"id":102,"obj":"btn","x":120,"y":210,"w":80,"h":25,"action":"back","bg_color":"#1fa3ec","radius":10,"border_side":1,"text":"\uF015","text_font":"montserrat-20"}
    +{"id":103,"obj":"btn","x":220,"y":210,"w":80,"h":25,"action":"next","bg_color":"#1fa3ec","radius":10,"border_side":1,"text":"\uF054","text_font":"montserrat-20"}
    +

    Page 1 is the default page, and contains different widgets types: labels and arc. The values are changed at runtime via Tasmota's rule system, see below for details.

    {"page":1,"comment":"---------- Page 1 ----------"}
    +{"id":0,"bg_color":"#0000A0","bg_grad_color":"#000000","bg_grad_dir":1,"text_color":"#FFFFFF"}
    +
    +{"id":2,"obj":"arc","x":20,"y":65,"w":80,"h":100,"border_side":0,"type":0,"rotation":0,"start_angle":180,"end_angle":0,"start_angle1":180,"value_font":12,"value_ofs_x":0,"value_ofs_y":-14,"bg_opa":0,"text":"--.-°C","min":200,"max":800,"val":0,"val_rule":"ESP32#Temperature","val_rule_formula":"val * 10","text_rule":"ESP32#Temperature","text_rule_format":"%2.1f °C"}
    +
    +{"id":5,"obj":"label","x":2,"y":35,"w":120,"text":"Temperature","align":1}
    +
    +{"id":10,"obj":"label","x":172,"y":35,"w":140,"text":"MPU","align":0}
    +{"id":11,"obj":"label","x":172,"y":55,"w":140,"text":"x=","align":0,"text_rule":"MPU9250#AX","text_rule_format":"x=%6.3f","text_rule_formula":"val / 1000"}
    +{"id":12,"obj":"label","x":172,"y":75,"w":140,"text":"y=","align":0,"text_rule":"MPU9250#AY","text_rule_format":"y=%6.3f","text_rule_formula":"val / 1000"}
    +{"id":13,"obj":"label","x":172,"y":95,"w":140,"text":"z=","align":0,"text_rule":"MPU9250#AZ","text_rule_format":"z=%6.3f","text_rule_formula":"val / 1000"}
    +

    Page 2 contains custom widgets as Berry code. These widgets are imported within haspmota_widgets.tapp

    {"page":2,"comment":"---------- Page 2 ----------"}
    +{"id":0,"bg_color":"#0000A0","bg_grad_color":"#000000","bg_grad_dir":1,"text_color":"#FFFFFF"}
    +
    +{"comment":"---------- Wifi status ----------"}
    +{"id":20,"obj":"lv_wifi_graph","x":257,"y":25,"w":60,"h":40,"radius":0}
    +{"id":21,"obj":"lv_tasmota_info","x":3,"y":25,"w":251,"h":40,"radius":0}
    +{"id":22,"obj":"lv_tasmota_log","x":3,"y":68,"w":314,"h":90,"radius":0,"text_font":12}
    +

    Finally, the following line allows to run arbitrary Berry code.

    {"comment":"--- Trigger sensors every 2 seconds ---","berry_run":"tasmota.add_cron('*/2 * * * * *', def () tasmota.publish_rule(tasmota.read_sensors()) end, 'oh_every_5_s')"}
    +

    The code trigger a read of sensors every 2 seconds and publish the JSON result to be parsed by the rule engine.

    tasmota.add_cron('*/2 * * * * *', def () tasmota.publish_rule(tasmota.read_sensors()) end, 'oh_every_5_s')
    +

    Running HASPmota~

    HASPmota code is included in tasmota32-lvgl firmwares.

    Running HASPmota with your own template is as simple as:

    • create a template in pages.jsonl and store it in the Tasmota file system
    • create an autoexec.be file containing the following:
    # simple `autoexec.be` to run HASPmota using the default `pages.jsonl`
    +import haspmota
    +haspmota.start()
    +

    HASPmota reference~

    Integration to Berry~

    objects as p<x>b<y>~

    Each HASPmota widget is mapped to a global variable of name p<x>b<y>. Example: p1b10. Such objects can be directly used via their attributes.

    Example:

    p1b10.x += 10
    +p1b10.text = "Hello"
    +print(p1b10.w)
    +

    pages as p<x>~

    Pages objects are mapped to a global variable of name p<x>.

    Changing pages can be done with p2.show()

    additional parsing~

    HASPmota parses all lines from the file pages.jsonl. You can dynamically add new objects as JSON with haspmota.parse(<json>). This functions takes a single json line. It is highly recommended to specify the page id in the json, otherwise the object is added to the current page.

    Pages~

    Pages object are identified by object if 0. Example:

    {"page":1,"id":0,"bg_color":"#0000A0","bg_grad_color":"#000000","bg_grad_dir":1,"text_color":"#FFFFFF"}
    +

    Internally HASPmota pages are implemented with LVGL screens, i.e. a parent object.

    Page 0 is a special page that is displays over every screens. It is the perfect place to put navigation menus. It is implement as lv.layer_top().

    Page 1 is always present and the default page.

    Attributes specific to page Details
    prev (int) target page number when pressing PREV button
    next (int) target page number when pressing NEXT button
    home (int) target page number when pressing HOME button
    And generally all object attributes

    Classes of widgets~

    Attribute "obj" can take the following values:

    HASPmota Class Embedded LVGL class
    obj lv.obj
    btn lv.btn
    switch lv.switch
    checkbox lv.checkbox
    label lv.label
    spinner lv.spinner
    line lv.line
    img lv.img
    dropdown lv.dropdown
    roller lv.roller
    btnmatrix lv.btnmatrix
    bar lv.bar
    slider lv.slider
    arc lv.arc
    textarea lv.textarea
    qrcode lv.qrcode

    You can also import custom widget as long as they inherit from lv.obj and the class name matches the module name.

    Example: "obj":"lv_wifi_graph" will trigger the following: - import lv_wifi_graph - instantiation of lv_wifi_graph(parent) object - if successful, it can be used like a typical HASPmota object

    Attributes~

    Below are the standard attributes:

    Attribute name LVGL equivalent Details
    comment Ignored
    meta any Any data, can be retrieved from Berry code with p<x>b<y>.meta
    page 0 to 15
    Parent screen object
    Page id for the current object.
    If not present, takes the value of the current page
    id 0..255 Id number of the object. Id 0 means the entire page.
    A global berry object is created with name p<page>b<id> (ex: p1b10)
    obj widget class Class of the widget (see above).
    If not present, the entire JSON line is ignored
    action "next", "prev", "back" or "p<x>" Switch to page when the object is touched
    x x X coordinate of top left corner
    y y Y coordinate of top left corner (Y is pointing down)
    h height Height in pixels
    w width Width in pixels
    hidden flag lv.OBJ_FLAG_HIDDEN Object is hidden (bool)
    enabled flag lv.OBJ_FLAG_CLICKABLE Object is touch/clickable (bool)
    click flag lv.OBJ_FLAG_CLICKABLE Synonym of enabled. Object is touch/clickable (bool)
    toggle flag lv.STATE_CHECKED When enabled, creates a toggle-on/toggle-off button. If false, creates a normal button.
    TODO check if bool or string
    radius style_radius Radius of rounded corners
    bg_opa style_bg_opa Opacity: 0 is transparent, 255 is opaque
    bg_color style_bg_color Color of background, format is #RRGGBB
    bg_grad_color style_bg_grad_color Color of background gradient
    bg_grad_dir style_bg_grad_dir Gradient direction
    0: none
    1: Vertical (top to bottom) gradient
    2: Horizontal (left to right) gradient
    border_side style_border_side Borders to be displayed (add all values)
    0: none
    1: bottom
    2: top
    4: left
    8: bottom
    15: full (all 4)
    border_width style_border_width Width of border in pixels
    border_color style_border_color
    line_color style_line_color Color of line
    line_width style_line_width
    line_width1 style_arc_width Sets the line width of an arc indicator part
    pad_left style_pad_left Left padding in pixels
    pad_right style_pad_right Right padding in pixels
    pad_top style_pad_top Top padding in pixels
    pad_bottom style_pad_bottom Bottom padding in pixels
    pad_all style_pad_all Sets all 4 padding values at once (Write-only)
    Attribute name LVGL equivalent Details
    text text Sets the inner text of the object.
    If the native LVGL object does not support text (like lv.btn), a lv.label sub-object is automatically created.
    value_str text Synonym of text
    align style_text_align Set alignment for text
    0 or "left": lv.TEXT_ALIGN_LEFT
    1 or "center": lv.TEXT_ALIGN_CENTER
    2 or "right": lv.TEXT_ALIGN_RIGHT
    text_font style_text_font Sets the font name and size for the text.
    If int, the default font is robotocondensed_latin1 and the parameter sets the size
    If string, the font is in the form <font_name>-<font_size>, example: montserrat-20 or in the form A:<font_file> to load a binary font from the file-system.
    value_font style_text_font Synonym of text_font
    text_color style_text_color Sets the color of text
    value_color style_text_color Synonym of text_color
    value_ofs_x x of sub-label Sets the X offset in pixels within the object
    value_ofs_y y of sub-label Sets the Y offset in pixels within the object
    text_rule Link the text to a Tasmota rule, see below
    text_rule_formula Link the text to a Tasmota rule, see below
    text_rule_format Link the text to a Tasmota rule, see below
    Attribute name LVGL equivalent Details
    min range Set the minimum value of range (int)
    max range Set the maximum value of range (int)
    val value Set the value (int)
    val_rule Link a value to a Tasmota rule, see below
    val_rule_formula Link a value to a Tasmota rule, see below

    Attributes specific to switch~

    Attribute name LVGL equivalent Details
    bg_color10 bg_color Color of the indicator when the switch is selected. Otherwise it uses bg_color.
    bg_color20 bg_color Color of the knob.
    radius20 radius Radius of the knob.

    Attributes specific to arc~

    Attribute name LVGL equivalent Details
    start_angle bg_start_angle Start angle of the arc background.
    Angles are in degrees in [0;360] range.
    Zero degrees is at the middle right (3 o'clock) of the object and the degrees are increasing in clockwise direction.
    end_angle bg_end_angle End angle of the arc background.
    start_angle1 start_angle Start angle of the arc indicator.
    end_angle1 end_angle End angle of the arc indicator.
    rotation rotation Offset to the 0 degree position
    type mode Sets the arc mode
    0: lv.ARC_MODE_NORMAL
    1: lv.ARC_MODE_REVERSE
    2: lv.ARC_MODE_SYMMETRICAL
    pad_top2 style_pad_top Top padding for lv.PART_KNOB part
    pad_bottom2 style_pad_bottom Bottom padding for lv.PART_KNOB part
    pad_left2 style_pad_left Left padding for lv.PART_KNOB part
    pad_right2 style_pad_right Right padding for lv.PART_KNOB part
    pad_all2 style_pad_all Set all 4 padding for lv.PART_KNOB part (write-only)
    radius2 style_radius Radius for lv.PART_KNOB part

    Attributes specific to img~

    Attribute name LVGL equivalent Details
    src src Path to the image in the file-system
    image_recolor style_image_recolor Color used to recolor the image
    image_recolor_opa style_image_recolor_opa Opacity of image recoloring
    angle angle Angle of the image, in 1/10th of degrees. Range 0..3600.

    Attributes specific to roller~

    Attribute name LVGL equivalent Details
    text selected_text (read-only) Get the text of the currently selected item. The string is truncated to the first 256 bytes.

    Attributes specific to spinner~

    Attribute name LVGL equivalent Details
    angle The length of the spinning segment in degrees - can be changed after initial value since v12.1.1.1
    speed The time for 1 turn in ms - can be changed after initial value since v12.1.1.1

    Attributes specific to qrcode~

    Attribute name LVGL equivalent Details
    qr_size Size in pixels of the QRCode, always square (height = width). This cannot be changed once the object is created.
    qr_dark_color Color used for the dark pixels. This cannot be changed once the object is created.
    qr_light_color Color used for the light pixels. This cannot be changed once the object is created.
    qr_text (mandatory) String to encode as a QRCode, can be changed at any moment.

    Tasmota extensions~

    Update sensor value via rules~

    You can automatically adjust attributes val and text from sensor values via a simple rule engine.

    attribute description
    val_rule Rule pattern to trigger an update of the val attribute.
    Example: "val_rule":"ESP32#Temperature"
    val_rule_formula Optional expression (using Berry) to transform the value extracted from the rule to the value pushed to val. This typically allows to adjust ranges of values.
    Input value is always converted to float not int. The input value is named val.
    Example: "val_rule_formula":"val / 1000"

    Changing a text attribute from rules:

    attribute description
    text_rule Rule pattern to trigger an update of the text attribute.
    Example: "text_rule":"ESP32#Temperature"
    text_rule_formula Optional expression (using Berry) to transform the value extracted from the rule to the value pushed to val. This typically allows to adjust ranges of values.
    Input value is always converted to float not int. The input value is named val.
    Example: "val_rule_formula":"val * 10"
    text_rule_format String format of the result string. The format uses Berry's string.format(), which is a subset of printf format.
    Example: "text_rule_format":"%2.1f °C"

    React to user actions~

    Every time the user touches an active element on the screen, HASPmota publishes internal events you can listen and react to. For example if you press a button p1b10, HASPmota publishes an event {"hasp":{"p1b10":{"event":"up"}} when the button is released. You can easily create a rule to react to this event.

    Example:

    tasmota.add_rule("hasp#p1b10#event=up", / -> print("Button p1b10 pressed"))
    +

    Events with value changed indicate that the value of the widgets has changed. Events are only fired if the change in value comes from a screen interaction. No event is fired when the value was changed programmatically. The new value is sent as val attribute, and for specific widgets (roller) a text attribute is sent for the label of the value. {"hasp":{"p1b1":{"val":3,"text":"A3","event":"changed"}}}

    Example:

    tasmota.add_rule("hasp#p1b1#text", / text-> print("p1b1 text value=", text))
    +

    Run arbitrary Berry code~

    Inserting an attribute berry_run to any object will compile and run the embedded Berry code right after widget initialization. If you need the current widget object, it requires a special construct like: "berry_run":"return def (x) print(x) end"

    One common use is to trigger sensors read every 2 seconds:

    {"comment":"--- Trigger sensors every 2 seconds ---","berry_run":"tasmota.add_cron('*/2 * * * * *', def () tasmota.publish_rule(tasmota.read_sensors()) end, 'oh_every_5_s')"}
    +

    HASPmota fonts~

    HASPmota can use 3 types of LVGL fonts:

    • embedded fonts, i.e. fonts included in Tasmota firmware
    • binary bitmat fonts (extension .lvfont), stored in the Tasmota file-system using lv_font_conv tool. Tasmota includes various fonts pre-converted
    • TrueType fonts (extension .ttf), stored in the Tasmota file-system. TrueType vector fonts can be scaled at any size and render well at high font-sizes, but require significant PSRAM memory

    Embedded fonts~

    Use attribute "text_font":"unscii-8". The general form is "text_font":"<font_name>-<font_size>"

    Embedded font Details
    montserrat-10
    montserrat-14
    montserrat-20
    montserrat-28
    Default LVGL normal font, including icons
    unscii-8
    unscii-16
    Default LVGL, 8 px and 16 px pixel perfect font with only ASCII characters
    seg7-8
    seg7-10
    seg7-12
    seg7-14
    seg7-16
    seg7-18
    seg7-20
    seg7-24
    seg7-28
    seg7-36
    seg7-48
    7 segments display, contains digits, space, ':' and '!' for a space of the size of ':'
    Sizes 8, 10, 12, 14, 16, 18 are pixel-perfect bold
    Sizes 20, 24, 28, 36, 48 are dithered (2 bits per pixel) bold-italic
    robotocondensed-12
    robotocondensed-16
    robotocondensed-24
    Default OpenHASP

    Binary bitmap fonts~

    Files of extension .lvfont, they are similar to embedded fonts but can be loaded dynamically from the file-system.

    Generally speaking, binary bitmap fonts are mostly used for low resolution font, or they take too much memory. For larger font, prefer TrueType fonts (see below).

    Pixel-perfect fonts~

    Pixel-perfect fonts don't use dithering and are specially designed for low resolution monochrome displays.

    example: pixel_perfect_1

    Same image zoomed x2 pixel_perfect_1x2

    unscii-8 (bottom font) is embedded by default.

    To use the other fonts, add the corresponding file to the Tasmota file system.

    Fonts below are from KreativeKorp

    • Berkelium64.lvfont Berkelium (BSW) GEOS System Font
    • PrintChar21.lvfont The Ultimate Apple II Font
    • Shaston320.lvfont Shaston GS/OS System Font

    Fonts below are from Daniel Linssen

    Here is the HASPmota template for the screen above:

    {"page":0,"comment":"---------- Upper stat line ----------"}
    +{"id":0,"text_color":"#FFFFFF"}
    +{"id":11,"obj":"label","x":0,"y":0,"w":320,"pad_right":90,"h":22,"bg_color":"#D00000","bg_opa":255,"radius":0,"border_side":0,"text":"Tasmota","text_font":"montserrat-20"}
    +
    +{"id":15,"obj":"lv_wifi_arcs","x":291,"y":0,"w":29,"h":22,"radius":0,"border_side":0,"bg_color":"#000000","line_color":"#FFFFFF"}
    +{"id":16,"obj":"lv_clock","x":232,"y":3,"w":55,"h":16,"radius":0,"border_side":0}
    +
    +{"page":1,"comment":"---------- Page 1 ----------"}
    +{"id":5,"obj":"label","x":2,"y":30,"w":316,"text":"Berkelium 74192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"A:Berkelium64.lvfont"}
    +{"id":6,"obj":"label","x":2,"y":55,"w":316,"text":"PrintChar21 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"A:PrintChar21.lvfont"}
    +{"id":7,"obj":"label","x":2,"y":80,"w":316,"text":"Shaston320 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"A:Shaston320.lvfont"}
    +{"id":8,"obj":"label","x":2,"y":105,"w":316,"text":"m5x7 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"A:m5x7.lvfont"}
    +{"id":9,"obj":"label","x":2,"y":130,"w":316,"text":"m3x6 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"A:m3x6.lvfont"}
    +
    +{"id":99,"obj":"label","x":2,"y":170,"w":316,"text":"unscii-8 192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"unscii-8"}
    +

    TrueType fonts~

    Add support for TTF fonts in HASPmota. The attributes needs to specify the font name and the size "text_font":"sketchbook.ttf-32"

    Example:

    sketchbook-32

    Related pages.jsonl file:

    {"page":0,"comment":"---------- Upper stat line ----------"}
    +{"id":0,"text_color":"#FFFFFF"}
    +{"id":11,"obj":"label","x":0,"y":0,"w":320,"pad_right":90,"h":22,"bg_color":"#D00000","bg_opa":255,"radius":0,"border_side":0,"text":"Tasmota","text_font":"montserrat-20"}
    +
    +{"id":15,"obj":"lv_wifi_arcs","x":291,"y":0,"w":29,"h":22,"radius":0,"border_side":0,"bg_color":"#000000","line_color":"#FFFFFF"}
    +{"id":16,"obj":"lv_clock","x":232,"y":3,"w":55,"h":16,"radius":0,"border_side":0}
    +
    +{"page":1,"comment":"---------- Page 1 ----------"}
    +{"id":1,"obj":"label","x":2,"y":40,"w":316,"text":"sketchbook-32\n192.168.x.x ABCDEF\nThe quick brown fox jumps over the lazy dog","text_font":"sketchbook.ttf-32"}
    +
    \ No newline at end of file diff --git a/HC-SR04/index.html b/HC-SR04/index.html new file mode 100644 index 0000000000..060359b493 --- /dev/null +++ b/HC-SR04/index.html @@ -0,0 +1,5 @@ + HC-SR04 ultrasonic ranging sensor - Tasmota
    Skip to content

    HC-SR04 ultrasonic ranging sensor~

    This feature is included only in tasmota-sensors binary

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_SR04
    +#define USE_SR04                            // Add support for HC-SR04 ultrasonic devices (+1k code)
    +    #define SR04_MAX_SENSOR_DISTANCE  500   // Set sensor max detection distance    #endif
    +

    HC-SR04 (HC-SR04P and JSN-SR04T) is an ultrasonic ranging sensor. Provides 2cm to 300cm of non-contact measurement functionality with a ranging accuracy that can reach up to 3mm. Each HC-SR04 module includes an ultrasonic transmitter, a receiver and a control circuit. See HC-SR04 Ultrasonic Sensor for more information.

    Buy example: - AliExpress - Sparkfun

    The US-100 can also be use as a HC-SR04 if you remove the jumper at the back (check Adafruit documentation).

    Configuration~

    Wiring~

    HC-SR04 ESP
    Vcc 5V
    GND GND
    Trig GPIOx
    Echo
    use voltage divider!
    GPIOy

    The HC-SR04 operates at 5V and therefore the echo signal will be 5V as well. Connecting the echo pin via a voltage divider (see schematic) will divide the 5V to 3.3V, which is the operating voltage of an ESP8266.

    There is a variant of the HC-SR04, called HC-SR04P. It mainly operates at 3.3V and then it doesn't need the voltage divider but you will notice a range drop of about 1 meter. image

    Tasmota Settings~

    In the Configuration -> Configure Module page assign:

    1. GPIOx to SR04 Tri (69)
    2. GPIOy to SR04 Ech (70)

    image

    Tasmota Main~

    After a reboot webUI displays the HC-SR04 distance.

    image

    Sensor sends a tele/%topic%/SENSOR JSON response:

    {"Time":"2019-01-01T22:42:35","SR04":{"Distance":16.754}}
    +

    \ No newline at end of file diff --git a/HDMI_CEC/index.html b/HDMI_CEC/index.html new file mode 100644 index 0000000000..e999abbf9c --- /dev/null +++ b/HDMI_CEC/index.html @@ -0,0 +1,27 @@ + HDMI CEC - Tasmota
    Skip to content

    HDMI CEC~

    This feature is experimental and still under development. It has only been tested on ESP8266.

    This feature is not included in standard Tasmota builds.

    When compiling your build add the following flag to the build environment or user_config_override.h:

    #define USE_HDMI_CEC
    +

    What is HDMI CEC?~

    HDMI CEC is a feature of HDMI designed to control HDMI connected devices by using only one remote controller; so, individual CEC enabled devices can command and control each other without user intervention, for up to 15 devices.

    This features enables a simple Tasmota device plugged to any HDMI port of your home equipment, to control the TV and other equipments, with a protocol more standard and robust than Infra-Red.

    Hardware needed~

    HDMI CEC needs only 3 GPIOs to be connected to an HDMI port. You can use a simple ESP8266 Wemos D1 Mini plugged with Dupont cables to an HDMI pass-through like these devices from Alix

    The GPIOs from ESP devices are electrically compatible with HDMI specifications, so you can simply connect GPIOs with no additional harware components:

    HDMI_CEC_template

    Wemos D1 Mini Configuration HDMI
    GPIO 2 HDMI CEC HDMI Pin 13
    GPIO 2 on Wemos D1 Mini is connected to a blue Led which is a visual indicator of CEC traffic
    GPIO 4 I2C SDA HDMI Pin 16
    GPIO 5 I2C SCL HDMI Pin 15
    Ground HDMI Pin 17
    +5V HDMI Pin 18
    If another device is present on the same HDMI port, it provides +5V with enough power for ESP8266. In such case you don't need an external power (tested with AppleTV).

    Below is the template:

    Template {"NAME":"HDMI CEC","GPIO":[1,1,9824,1,640,608,1,1,1,1,1,1,1,1],"FLAG":0,"BASE":18}

    Quick start and quick tour~

    HDMI CEC auto-detects all parameters and does not need specific configuration, although you may override the default configuration if needed. By default, Tasmota announces itself as a "Playback Device" on the HDMI bus.

    Logs during boot:

    00:00:00.002 HDW: ESP8266EX
    +00:00:00.005 UFS: FlashFS mounted with 1992 kB free
    +00:00:00.063 CFG: Loaded from File, Count 422
    +00:00:00.118 QPC: Reset
    +00:00:00.130 Project tasmota - Tasmota Version 13.1.0.2(tasmota-4M)-2_7_4_9(2023-09-01T20:20:26)
    +00:00:00.247 CEC: HDMI CEC initialized on GPIO 2, Logical address 8, Physical address 0x1000
    +

    Extra logs when Weblog 3:

    00:00:00.001 HDW: ESP8266EX
    +00:00:00.007 UFS: FlashFS mounted with 1992 kB free
    +00:00:00.066 CFG: Loaded from File, Count 426
    +00:00:00.072 QPC: Count 1
    +00:00:00.074 CFG: CR 342/699, Busy 0
    +00:00:00.082 SRC: Restart
    +00:00:00.083 Project tasmota - Tasmota Version 13.1.0.2(tasmota-4M)-2_7_4_9(2023-09-01T20:20:26)
    +00:00:00.084 CEC: trying to read physical address
    +00:00:00.110 CEC: successfully read EDID 256 bytes, extensions count 1
    +00:00:00.111 CEC: EDID: 00FFFFFFFFFFFF004DD9015C010101010112010380A05A780A0DC9A05747982712484C21080081800101010101010101010101010101023A801871382D40582C450040846300001E011D007251D01E206E28550040846300001E000000FC00534F4E592054562058560A2020000000FD00303E0E460F000A20202020202001E402032B70501F101405130412111615030207060120230907078301000066030C00100080E3050301E20039023A80D072382D40102C458040846300001E011D00BC52D01E20B828554040846300001E011D8018711C1620582C250040846300009E011D80D0721C1620102C258040846300009E000000000000000000000000CA
    +00:00:00.111 CEC: physical address found: 0x1000
    +00:00:00.152 CEC: Packet sent: 44 ACK
    +00:00:00.199 CEC: Packet sent: 88 NAK
    +00:00:00.200 CEC: HDMI CEC initialized on GPIO 2, Logical address 8, Physical address 0x1000
    +00:00:00.678 CEC: Packet sent: 8F84100004 ACK
    +

    In the above example, the first part probes I2C to get EDID data and compute the physical address. Tasmota sends 44 message to check if Logical address 4 is in use. Since the message is ACKed it is in use (here by an AppleTV plugged on the same HDMI port). It tries address 8 with packet 88. NAK indicates that the address is free, so it is claimed by Tasmota. The message 8F84100004 indicates to the bus that logical device 8 has the physical address 0x1000, i.e. connected the HDMI port 1 of TV.

    Commands~

    Command Parameters
    HdmiType : set the CEC device type (0..15 as per CEC specs)
    The default value is 4 (Playback Device). Changes require a restart to renegociate a new HDMI logical address.
    Possible values are:
    0: TV
    1: Recording Device
    2: Reserved
    3: Tuner
    4: Playback Device
    5: Audio System
    HdmiSend Send a payload to the TV or to any device
    HdmiSend <hex>: sends the <hex> payload to the TV
    HdmiSend { ["To": <to>, ] "Data":"<hex>"} gives more control about the target.
    <to> is the logical address of the targer, default is 0 which is the TV
    <hex> is the payload
    Examples:
    HdmiSend 8F or HdmiSend {"Data":"8F"} - ask the power status of the TV
    HdmiSend {"To":4,"Data":"8C} - ask its vendor id to logical address 4

    Receiving and parsing payloads~

    Currently payloads are passed as HEX data without any encoding/decoding.

    Whenever a message is received on the CEC bus, a payload using the following syntax is triggered and can be matched with a Rule. Only messages addressed to Tasmota are published as payloads in MQTT, all messages not adressed to Tasmota are masked to avoid generating too much traffic. Note: the total traffic can be monitored with loglevel 3.

    Received payloads generate an Rule event as follows:

    {"HdmiReceived":{"From":<from>,"To":<to>,"Data":"<hex>"}}
    +

    Example: command HdmiSend 8F

    20:07:59.449 CMD: HdmiSend 8F
    +20:07:59.454 RSL: RESULT = {"HdmiSend":"Done"}
    +20:07:59.632 RSL: SENSOR = {"HdmiReceived":{"From":0,"To":8,"Data":"9001"}}
    +
    In the above, Tasmota sends command 8F (Give Device Power Status) to query the power status of the TV. The response 9001 (Report Power Status) indicates that the TV is in Standby mode.

    Turning the TV on~

    Command: HdmiSend 04

    Turning the TV on~

    Command: HdmiSend 36

    Advanced usage~

    When setting loglevel 3 (ex: WebLog 3), you see all messages being published in the CEC bus. They all generate an internal JSON event HdmiReceived that can be used by a rule.

    The following commands are for advanced users or specific use-cases:

    Command Parameters
    HdmiSendRaw <hex>: send a raw payload to the HDMI CEC bus
    This gives full control over messages and allows to impersonate another device
    HdmiSendRaw 408F pretend that device at logical address 4 asks TV for its power status.
    HdmiAddr : set the default HDMI Physical Address in case it cannot be discovered.
    The default value is 0x1000 which is HDMI Port 1 on the TV
    This value is only used if the I2C port is not connected or if the discovery failes.
    If no argumant, this command returns the current physical adress.

    The CEC-O-MATIC tool is handy to decode and encode payloads.

    \ No newline at end of file diff --git a/HM-10/index.html b/HM-10/index.html new file mode 100644 index 0000000000..964de48901 --- /dev/null +++ b/HM-10/index.html @@ -0,0 +1,7 @@ + HM-10 Bluetooth module - Tasmota
    Skip to content

    HM-10 Bluetooth module~

    This feature is included only in tasmota-sensors

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_HM10
    +#define USE_HM10          // Add support BLE sensor bridge (+5k1 code)
    +#endif
    +#ifndef USE_IBEACON
    +#define USE_IBEACON          // Add support for bluetooth LE passive scan of ibeacon devices 
    +#endif
    +

    HM-10 is a Bluetooth BLE V4.0 module based on TI CC254x chip. Originally manufactured by Jinan Huamao but there are many clones in the market (MLT-BT05, CC41-A, JDY-08, AT-09, ...)

    While they look similar and utilize the same CC254x Bluetooth chip, the firmware on them is different. In order to get any type of module to work work Tasmota it is mandatory to flash the original firmware on it. Use the newest V710 firmware but at least V609 is required to work with the Tasmota driver.

    Tip

    It is possible to do this without soldering by directly sticking male jumper wire connectors through the plastic foil on some boards or using some removable adhesives (bluetack).

    Configuration~

    Check whether your module is communicating properly over its TX/RX pins and that it has the required firmware (preferably HMSoft V707). Only when you're sure everything is ok proceed with the configuration.

    Wiring~

    HM-10 ESP
    GND GND
    VCC 3.3V
    TX GPIOx
    RX GPIOy

    You can use TX and RX pins for GPIOx, GPIOy.

    Tasmota Settings for BLE sensors~

    In the Configuration -> Configure Module page assign:

    1. GPIOx to HM10 TX (195)
    2. GPIOy to HM10 RX (194)

    If you have the proper firmware, webUI in the Main Menu should show (in this case V707 is the flashed firmware version): Config success

    If you have supported Bluetooth devices in range, they will soon be discovered and start to populate the webUI with data as it is transmitted:

    Showing data

    Warning

    Most of the self-flashed modules will still have their factory default settings with baud rate of 9600 but Tasmota requires 115200. To fix try commands from Tasmota console: HM10Baud 9600 (sets 9600 baud rate on Tasmota side) then HM10AT RENEW (reset HM10 to factory settings which should use 115200 baud rate on newer firmware) and reboot Tasmota. If that doesn't solve it you will have to connect to the HM-10 with serial-to-USB adapter and set the baudrate in a terminal using commands AT+RENEW then AT+BAUD?. If the output is 4 baudrate is properly set to 115200, if not use AT+BAUD4

    For a complete overview of supported devices, commands and features read the Bluetooth article

    Tasmota Settings for iBeacon~

    In the Configuration -> Configure Module page assign:

    1. GPIOx to iBeacon TX (168)
    2. GPIOy to iBeacon RX (169)

    When first connected HM-10 is in peripheral mode. You have to change it to central mode using commands Sensor52 1 and Sensor52 2.

    If you have supported iBeacon Bluetooth devices in range, they will be discovered and will start to populate the webUI with data:

    Showing data

    For a complete overview of supported devices, commands and features read the Bluetooth article

    Breakout Boards~

    HM-10 BreakoutHM-10 Breakout

    \ No newline at end of file diff --git a/HM-17/index.html b/HM-17/index.html new file mode 100644 index 0000000000..9e36b2f830 --- /dev/null +++ b/HM-17/index.html @@ -0,0 +1,7 @@ + HM-17 Bluetooth module - Tasmota
    Skip to content

    HM-17 Bluetooth module~

    This feature is included only in tasmota-sensors

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_HM10
    +#define USE_HM10          // Add support BLE sensor bridge (+5k1 code)
    +#endif
    +#ifndef USE_IBEACON
    +#define USE_IBEACON          // Add support for bluetooth LE passive scan of ibeacon devices 
    +#endif
    +

    HM-17 and HM-16 are a Bluetooth BLE V4.1 module based on TI CC2640R2f chip. Manufactured by Jinan Huamao

    Configuration~

    Check whether your module is communicating properly over its TX/RX pins and that it has the required firmware (firmware V120 or V110). Only when you're sure everything is ok proceed with the configuration.

    Wiring~

    HM-16/17 ESP
    GND GND
    VCC 3.3V
    TX GPIOx
    RX GPIOy

    HM-16 PinoutHM-17 Pinout

    Tasmota Settings for iBeacon~

    In the Configuration -> Configure Module page assign:

    1. GPIOx to iBeacon TX (168)
    2. GPIOy to iBeacon RX (169)

    When first connected HM-16/17 is in peripheral mode. You have to change it to central mode using commands Sensor52 1 and Sensor52 2.

    If you have supported iBeacon Bluetooth devices in range, they will be discovered and will start to populate the webUI with data:

    Showing data

    For a complete overview of supported devices, commands and features read the Bluetooth article

    Breakout Boards~

    HM-10 BreakoutHM-10 Breakout

    \ No newline at end of file diff --git a/HRG15/index.html b/HRG15/index.html new file mode 100644 index 0000000000..2e8edcb3a5 --- /dev/null +++ b/HRG15/index.html @@ -0,0 +1,14 @@ + Hydreon RG-15 Solid State Rain Sensor - Tasmota
    Skip to content

    Hydreon RG-15 Solid State Rain Sensor~

    This feature is not included in precompiled binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_HRG15
    +#define USE_HRG15    // Add support for Hydreon RG-15 Solid State Rain sensor (+1k5 code)
    +#endif
    +

    The Hydreon RG-15 Solid State Tipping Bucket is a rainfall measuring device intended to replace conventional tipping buckets.

    The RG-15 uses beams of infrared light within a plastic lens about the size of a tennis ball. The round surface of the lens discourages collection of debris, and the RG-15 has no moving parts to stick, and no water-pathways to clog. The device features an open-collector output that emulates a conventional tipping bucket, as well as serial communications that provide more detailed data and allow for configuration of the device.

    Find out more on the manufacturer's website.

    Configuration~

    Wiring~

    HRG15 ESP
    GND (1) GND
    V+ 3.3V (8) 3.3V
    RS232 Out (4) GPIOx
    RS232 In (5) GPIOy

    Tasmota Settings~

    In the Configuration -> Configure Module page assign:

    • GPIOx to HRG15 Rx
    • GPIOy to HRG15 Tx

    Commands~

    Sensor90 can be used to configure the rain sensor

    Tasmota Display~

    After a reboot the driver will detect the sensor and show the sensor data. An example is given below:

    Item Value
    RG-15 Active 0.01 mm
    RG-15 Event 0.13 mm
    RG-15 Total 26.8 mm
    RG-15 FlowRate 0.32 mm.h

    This data is also part of the tele/%topic%/SENSOR payload:

    {
    +    "Time": "2021-08-25T17:15:45",
    +    "RG-15": {
    +        "Active": 0.01,
    +        "Event": 0.13,
    +        "Total": 26.80,
    +        "FlowRate": 0.32
    +    },
    +    "TempUnit": "C"
    +}
    +

    MQTT~

    The sensor data is made available over MQTT as per the configured TelePeriod. Additionally, instant telemetry messages are sent during active rainfall events. The sensor will report additional accumulation every 5-60 seconds. The driver resets the Active and FlowRate values to 0 if no additional accumuatlion is detected after 60 seconds.

    \ No newline at end of file diff --git a/Home-Assistant/index.html b/Home-Assistant/index.html new file mode 100644 index 0000000000..b88caaea2f --- /dev/null +++ b/Home-Assistant/index.html @@ -0,0 +1,664 @@ + Home Assistant - Tasmota
    Skip to content

    Home Assistant~

    Home Assistant is an open source home automation solution that puts local control and privacy first.

    Tasmota communicates with Home Assistant using MQTT. Before going any further, make sure MQTT is properly set up in Home Assistant and Tasmota.

    Home Assistant can add Tasmota devices using:

    1. Official Tasmota integration - preferred and automatic instant discovery of entities
    2. Manual configuration by editing configuration.yaml - recommended for marginal use cases, f.e. TuyaMCU fan devices

    Tasmota Integration~

    Open your Home Assistant instance and start setting up a new integration.

    Once you configure the Home Assistant integration every new Tasmota device with SetOption19 0 set will be discovered automatically.

    All Tasmota devices will be listed in their own Tasmota card in Configuration - Integrations menu in Home Assistant.

    Warning

    Do not change the FullTopic in order to use this feature. Leave it as default.

    Informative sensor

    Tasmota uses DeviceName to name the device in Tasmota integration and FriendlyName<x> to name power outputs (switch or light entities in HA).

    Use templates to change the Module name

    Setting the template name will change the Module name as well which will be reflected in Home Assistant. This helps in identifying when dealing with a large number of devices. It's nicer than having 10 Sonoff S31's or a bunch of TuyaMCU's" Template name as Module name in Home Assistant

    Bug

    If you are using Home Assistant OS MQTT add-on add the following to the ACL file (user section or general section): topic write tasmota/discovery/# to give Tasmota devices write access to the discovery topic.

    Supported Entities~

    • Lights as light entities.
      Single channel Dimmers, RGB lights, RGB lights with Color Temperature control and RGB lights with White control are supported.
    • Relays as switch entities With SetOption30 1 all relays one the device will be added as light entities instead
    • Sensors as sensor entities
    • Switches as binary_sensor entities or automation triggers depending on SwitchMode used when SetOption114 1
    • Buttons as automation triggers when SetOption73 is enabled
    • Shutters as cover entities. Currently only shutter modes 1 to 4 are supported. Shutter mode 5 and Tuya shutters are not supported.
    • Devices configured as iFan02 or iFan03 as fan entities. TuyaMCU fans are not supported.
    • Each discovered device will by default add 8 disabled sensors. Enable the ones you need. You cannot delete them, they will simply be re-added on a restart.

    Types of devices not listed above still require manual configuration.

    If you're running Tasmota on ESP32 you can use Home Assistant Controls in Berry to discover more complex entities (pull-down lists, number sliders, etc)

    Zigbee and Bluetooth devices paired in Tasmota will NOT be discovered in Home Assistant

    Removing devices~

    You cannot as long as Home Assistant support is enabled in your Tasmota binary. Disable the device in Home Assistant instead.

    Disable device

    Deleting them from Home Assistant while Tasmota device is still active will eventually rediscover all the entities.

    Supplemental Custom Discovery Message~

    You can use MQTT Discovery in HA to add sensors and other entities that are not discovered through Tasmota integration and associate them with an already discovered device.

    When creating the MQTT discovery JSON add this device identifier ,"device":{"connections":[["mac","%macaddr%"]]} where %macaddr% is the mac address of the device without :. When used in a rule variable %macaddr% will be replaced automatically.

    There are Home Assistant Blueprints for specific device types to create needed entities at out blueprints GitHub. Look for the icon in templates to one-click import the configuration automation.

    Examples of creating custom discovery messages:

    Editing configuration.yaml~

    Adding devices manually is done by editing the configuration.yaml file and adding appropriate blocks of yaml code to the corresponding section.

    After every change to the configuration.yaml file you'll need to restart the MANUALLY CONFIGURED MQTT ENTITIES in Server Controls to apply the changes.

    The advantage of manually configuring a device is that you maintain control of all aspects of the configuration.

    All the configurations are just examples.

    You need to be familiar with Home Assistant's configuration structure and procedures.
    Straight copy paste of the given examples into configuration.yaml will probably not work for you.

    If you are using a localized (non-english) version be sure to check the correct spelling and cases for values:

    • 'payload_available'
    • 'payload_not_available'
    • 'payload_on'
    • 'payload_off'

    Tip

    If you want the power states to be persistent in Tasmota and Home Assistant set PowerRetain 1 instead of using retain: true in Home Assistant

    When using unique_id: make sure it's unique to each entity

    When unique_id is set, Home Assistant will allow some entity customization from the UI such as changing the name or icon.

    Switches~

    Add in Home Assistant using the MQTT Switch integration.

    Required Commands
    SetOption59 1 - enables sending of tele/%topic%/STATE on POWER and light related commands for faster updates

    Single Switch

    If SetOption26 is enabled, use POWER1 instead of POWER

    mqtt:
    +  switch:
    +    - unique_id: tasmota_switch
    +      name: "Tasmota Switch"
    +      state_topic: "stat/tasmota/RESULT"  
    +      value_template: "{{ value_json.POWER }}"
    +      command_topic: "cmnd/tasmota/POWER"
    +      payload_on: "ON"
    +      payload_off: "OFF"
    +      availability_topic: "tele/tasmota/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +

    Multiple Switches

    When a device has more than one POWER output (multiple relays or a mix of relays and lights) use corresponding POWER<x> (POWER1, POWER2, etc)

    mqtt:
    +  switch:
    +  - unique_id: tas_switch_1
    +    name: "Tasmota Switch 1"
    +    state_topic: "stat/tasmota/RESULT"  
    +    value_template: "{{ value_json.POWER1 }}"
    +    command_topic: "cmnd/tasmota/POWER1"
    +    payload_on: "ON"
    +    payload_off: "OFF"
    +    availability_topic: "tele/tasmota/LWT"
    +    payload_available: "Online"
    +    payload_not_available: "Offline"
    +  - unique_id: tas_switch_2
    +    name: "Tasmota Switch 2"
    +    state_topic: "stat/tasmota/RESULT"  
    +    value_template: "{{ value_json.POWER2 }}"
    +    command_topic: "cmnd/tasmota/POWER2"
    +    payload_on: "ON"
    +    payload_off: "OFF"
    +    availability_topic: "tele/tasmota/LWT"
    +    payload_available: "Online"
    +    payload_not_available: "Offline"
    +  - unique_id: tas_switch_3
    +    name: "Tasmota Switch 3"
    +    state_topic: "stat/tasmota/RESULT"  
    +    value_template: "{{ value_json.POWER3 }}"
    +    command_topic: "cmnd/tasmota/POWER3"
    +    payload_on: "ON"
    +    payload_off: "OFF"
    +    availability_topic: "tele/tasmota/LWT"
    +    payload_available: "Online"
    +    payload_not_available: "Offline"
    +

    Tip

    If you are using a switch device to control a light you may want to use MQTT Light integration instead.

    Simply replace switch: with light: in the configuration keeping everything else the same.

    Lights~

    Add in Home Assistant using the MQTT Light integration.

    All configurations require SetOption59 1 - enables sending of tele/%topic%/STATE on POWER and light related commands

    Optional Commands
    Fade on - makes transitions smoother
    Speed 5 - set transition speed

    Dimmer

    Used for dimmers and dimmable lights (single channel lights).

    mqtt:
    +  light:
    +    - name: "Dimmer"
    +      command_topic: "cmnd/tasmota/POWER"
    +      state_topic: "tele/tasmota/STATE"
    +      state_value_template: "{{value_json.POWER}}"
    +      availability_topic: "tele/tasmota/LWT"
    +      brightness_command_topic: "cmnd/tasmota/Dimmer"
    +      brightness_state_topic: "tele/tasmota/STATE"
    +      brightness_scale: 100
    +      on_command_type: "brightness"
    +      brightness_value_template: "{{value_json.Dimmer}}"
    +      payload_on: "ON"
    +      payload_off: "OFF"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +

    RGB Light

    Should also be used with lights using White Blend Mode

    SetOption17 1 - enables color status in decimals

    mqtt:
    +  light:
    +    - name: "RGB Light"
    +      command_topic: "cmnd/tasmota/POWER"
    +      state_topic: "tele/tasmota/STATE"
    +      state_value_template: "{{ value_json.POWER }}"
    +      availability_topic: "tele/tasmota/LWT"
    +      brightness_command_topic: "cmnd/tasmota/Dimmer"
    +      brightness_state_topic: "tele/tasmota/STATE"
    +      brightness_scale: 100
    +      on_command_type: "brightness"
    +      brightness_value_template: "{{ value_json.Dimmer }}"
    +      rgb_command_topic: "cmnd/tasmota/Color2"
    +      rgb_state_topic: "tele/tasmota/STATE"
    +      rgb_value_template: "{{ value_json.Color.split(',')[0:3]|join(',') }}"
    +      effect_command_topic: "cmnd/tasmota/Scheme"
    +      effect_state_topic: "tele/tasmota/STATE"
    +      effect_value_template: "{{ value_json.Scheme }}"
    +      effect_list:
    +        - 0
    +        - 1
    +        - 2
    +        - 3
    +        - 4
    +      payload_on: "ON"
    +      payload_off: "OFF"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +

    RGB+W Light

    In this configuration RGB and white cannot be on at the same time. See Lights for options.

    SetOption17 1 - enables color status in decimals

    mqtt:
    +  light:
    +    - name: "RGB+W Light"
    +      command_topic: "cmnd/tasmota/POWER"
    +      state_topic: "tele/tasmota/STATE"
    +      state_value_template: "{{ value_json.POWER }}"
    +      availability_topic: "tele/tasmota/LWT"
    +      brightness_command_topic: "cmnd/tasmota/Dimmer"
    +      brightness_state_topic: "tele/tasmota/STATE"
    +      brightness_scale: 100
    +      on_command_type: "brightness"
    +      brightness_value_template: "{{ value_json.Dimmer }}"
    +      white_value_state_topic: "tele/tasmota/STATE"
    +      white_value_command_topic: "cmnd/tasmota/White"
    +      white_value_scale: 100
    +      white_value_template: "{{ value_json.White }}"
    +      rgb_command_topic: "cmnd/tasmota/Color2"
    +      rgb_state_topic: "tele/tasmota/STATE"
    +      rgb_value_template: "{{ value_json.Color.split(',')[0:3]|join(',') }}"
    +      effect_command_topic: "cmnd/tasmota/Scheme"
    +      effect_state_topic: "tele/tasmota/STATE"
    +      effect_value_template: "{{value_json.Scheme}}"
    +      effect_list:
    +        - 0
    +        - 1
    +        - 2
    +        - 3
    +        - 4
    +      payload_on: "ON"
    +      payload_off: "OFF"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +

    RGB+CCT Light

    Also called RGBWW, RGBCW or 5 channel lights

    SetOption17 1 - enables color status in decimals

    mqtt:
    +  light:
    +    - name: "RGBCCT Light"
    +      command_topic: "cmnd/tasmota/POWER"
    +      state_topic: "tele/tasmota/STATE"
    +      state_value_template: "{{ value_json.POWER }}"
    +      availability_topic: "tele/tasmota/LWT"
    +      brightness_command_topic: "cmnd/tasmota/Dimmer"
    +      brightness_state_topic: "tele/tasmota/STATE"
    +      brightness_scale: 100
    +      on_command_type: "brightness"
    +      brightness_value_template: "{{ value_json.Dimmer }}"
    +      color_temp_command_topic: "cmnd/tasmota/CT"
    +      color_temp_state_topic: "tele/tasmota/STATE"
    +      color_temp_value_template: "{{ value_json.CT }}"
    +      rgb_command_topic: "cmnd/tasmota/Color2"
    +      rgb_state_topic: "tele/tasmota/STATE"
    +      rgb_value_template: "{{ value_json.Color.split(',')[0:3]|join(',') }}"
    +      effect_command_topic: "cmnd/tasmota/Scheme"
    +      effect_state_topic: "tele/tasmota/STATE"
    +      effect_value_template: "{{ value_json.Scheme }}"
    +      effect_list:
    +        - 0
    +        - 1
    +        - 2
    +        - 3
    +        - 4
    +      payload_on: "ON"
    +      payload_off: "OFF"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +

    Addressable LED

    Applies only to WS281x lights.

    mqtt:
    +  light:
    +    - name: "Addressable LED"
    +      command_topic: "cmnd/tasmota/POWER"
    +      state_topic: "stat/tasmota/STATE"
    +      state_value_template: "{{ value_json.POWER }}"
    +      availability_topic: "tele/tasmota/LWT"
    +      brightness_command_topic: "cmnd/tasmota/Dimmer"
    +      brightness_state_topic: "stat/tasmota/STATE"
    +      brightness_scale: 100
    +      on_command_type: "brightness"
    +      brightness_value_template: "{{ value_json.Dimmer }}"
    +      rgb_command_topic: "cmnd/tasmota/Color2"
    +      rgb_state_topic: "tele/tasmota/STATE"
    +      rgb_value_template: "{{ value_json.Color.split(',')[0:3]|join(',') }}"
    +      effect_command_topic: "cmnd/tasmota/Scheme"
    +      effect_state_topic: "stat/tasmota/STATE"
    +      effect_value_template: "{{ value_json.Scheme }}"
    +      effect_list:
    +        - 0
    +        - 1
    +        - 2
    +        - 3
    +        - 4
    +        - 5
    +        - 6
    +        - 7
    +        - 8
    +        - 9
    +        - 10
    +        - 11
    +        - 12
    +      payload_on: "ON"
    +      payload_off: "OFF"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +      qos: 1
    +      retain: false
    +

    RGB with hex values

    If you don't want to use SetOption17 1 with decimal values and use default hex values, change:

    rgb_value_template: "{{value_json.Color.split(',')[0:3]|join(',')}}"
    +
    to
      rgb_command_template: "{{ '%02X%02X%02X' | format(red, green, blue)}}"
    +  rgb_value_template: "{{ (value_json.Color[0:2]|int(base=16),value_json.Color[2:4]|int(base=16),value_json.Color[4:6]|int(base=16)) | join(',')}}"
    +

    Control RGB and White independently

    Using color picker will keep white light on. If you use the white slider RGB light will get turned off. White value and dimmer value are connected, to have more granular control you will have to split the lights.

    Replace

    rgb_value_template: "{{value_json.Color.split(',')[0:3]|join(',')}}"
    +
    to
      rgb_command_template: "{{ '%02X%02X%02X=' | format(red, green, blue)}}"
    +  rgb_value_template: "{{ (value_json.Color[0:2]|int(base=16),value_json.Color[2:4]|int(base=16),value_json.Color[4:6]|int(base=16)) | join(',')}}"
    +

    The key is the = after color string in hex. It will retain current white value while changing color.

    Using schema: template light

    Thorough explanation of template: schema lights and its features on blakadder.com

    Sensors~

    Add in Home Assistant using the MQTT Sensor integration.

    A sensor will send its data in set intervals defined by TelePeriod (default every 5 minutes).

    Temperature sensor

    Check your sensor name in Tasmota console and change accordingly. This example uses the DHT22 sensor.

    mqtt:
    +  sensor:
    +    - name: "Tasmota Temperature"
    +      state_topic: "tele/tasmota/SENSOR"
    +      value_template: "{{ value_json['DHT22'].Temperature }}"
    +      unit_of_measurement: "°C"  # "F" if using Fahrenheit
    +      availability_topic: "tele/tasmota/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +      device_class: temperature
    +

    Humidity sensor

    Check your sensor name in Tasmota and change accordingly. This example uses the DHT22 sensor.

    mqtt:
    +  sensor:
    +    - ame: "Tasmota Humidity"
    +      state_topic: "tele/tasmota/SENSOR"
    +      value_template: "{{ value_json['DHT22'].Humidity }}"
    +      unit_of_measurement: "%"
    +      availability_topic: "tele/tasmota/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +      device_class: humidity
    +

    Pressure sensor

    Check your sensor name in Tasmota and change accordingly. This example uses the BMP280 sensor.

    mqtt:
    +  sensor:
    +    - name: "Tasmota Pressure"
    +      state_topic: "tele/tasmota/SENSOR"
    +      value_template: "{{ value_json.BMP280.Pressure }}"
    +      unit_of_measurement: "hPa"
    +      device_class: pressure
    +
    Change unit_of_measurement to "mmHg" if SetOption24 1

    Wi-Fi Signal Quality

    Monitor the relative Wi-Fi signal quality of a device.

    sensor:
    +  - platform: mqtt
    +    name: "Tasmota Wi-Fi Quality"
    +    state_topic: "tele/tasmota/STATE"
    +    unit_of_measurement: "%"
    +    value_template: "{{value_json['Wifi'].RSSI }}"
    +    availability_topic: "tele/tasmota/LWT"
    +    payload_available: "Online"
    +    payload_not_available: "Offline"
    +    device_class: signal_strength
    +    entity_category: diagnostic # moves the entity to the Diagnostic section on the Device Card
    +

    Power Monitoring~

    Example of Home Assistant UI

    Add in Home Assistant using the MQTT Sensor integration.

    Power monitoring sensors will send their data in set intervals defined by TelePeriod (default every 5 minutes).

    To get all the data in Home Assistant requires multiple sensors which you can later group to your liking in Lovelace UI

    Power Monitoring

    mqtt:
    +  sensor:
    +    - name: "Energy Today"
    +      state_topic: "tele/tasmota/SENSOR"
    +      value_template: '{{ value_json["ENERGY"]["Today"] }}'
    +      unit_of_measurement: "kWh"
    +      device_class: energy
    +      state_class: measurement 
    +    - name: "Power"
    +      state_topic: "tele/tasmota/SENSOR"
    +      value_template: '{{ value_json["ENERGY"]["Power"] }}'
    +      unit_of_measurement: "W"
    +      device_class: power
    +      state_class: measurement 
    +    - name: "Voltage"
    +      state_topic: "tele/tasmota/SENSOR"
    +      value_template: '{{ value_json["ENERGY"]["Voltage"] }}'
    +      unit_of_measurement: "V"
    +      device_class: voltage
    +      state_class: measurement 
    +    - name: "Current"
    +      state_topic: "tele/tasmota/SENSOR"
    +      value_template: '{{ value_json["ENERGY"]["Current"] }}'
    +      unit_of_measurement: "A"
    +      device_class: current
    +      state_class: measurement 
    +

    Tip

    For additional sensors use "Total";"Yesterday";"Period","ApparentPower","ReactivePower";"Factor" in value_template string

    Video tutorial on a power monitoring plug setup by Digiblur

    Binary Sensors~

    Add in Home Assistant using the MQTT Binary Sensor integration.

    PIR Sensor

    Used for a configured PIR Sensor and requires correct SwitchMode with ON/OFF states and this rule:

    Required Commands

    Rule1 on Switch1#State=1 do Publish stat/%topic%/MOTION ON endon on Switch1#State=0 do Publish stat/%topic%/MOTION OFF endon
    +Rule1 1
    +
    mqtt:
    +  binary_sensor:
    +    - unique_id: motion_sensor
    +      name: "Tasmota Motion Sensor"
    +      state_topic: "stat/tasmota/MOTION"
    +      availability_topic: "tele/tasmota/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +      device_class: motion
    +

    Door Sensor

    Requires a reed switch configured in Tasmota. Make sure correct SwitchMode is set

    Required Commands

    Rule1 on Switch1#State=1 do Publish stat/%topic%/DOOR ON endon on Switch1#State=0 do Publish stat/%topic%/DOOR OFF endon
    +Rule1 1
    +
    mqtt:
    +  binary_sensor:
    +    - unique_id: door_sensor
    +      name: "Tasmota Door Sensor"
    +      state_topic: "stat/tasmota/GARAGE"
    +      availability_topic: "tele/tasmota/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +      device_class: door   # also possible: window, garage_door or opening
    +

    RF Bridge

    An RF door sensor configured with an RF receiver in Tasmota.

    mqtt:
    +  binary_sensor:
    +    - platform: mqtt
    +      name: "RF bridge rfkey"
    +      payload_on: "1"
    +      payload_off: "0"
    +      device_class: opening
    +      state_topic: "tele/tasmota/RESULT"
    +      value_template: '{{ value_json.RfReceived.RfKey }}'
    +

    Fans~

    Add in Home Assistant using the MQTT Fan integration.

    Fan

    Derived from #2839 by @kbickar and @finity69x2

    mqtt:
    +  fan:
    +  - unique_id: tasmota_fan
    +    name: "Tasmota Fan"
    +    command_topic: "cmnd/tasmota/FanSpeed"
    +    speed_command_topic: "cmnd/tasmota/FanSpeed"    
    +    state_topic: "stat/tasmota/RESULT"
    +    speed_state_topic: "stat/tasmota/RESULT"
    +    state_value_template: >
    +      {% if value_json.FanSpeed is defined %}
    +        {% if value_json.FanSpeed == 0 -%}0{%- elif value_json.FanSpeed > 0 -%}4{%- endif %}
    +      {% else %}
    +        {% if states.fan.tasmota.state == 'off' -%}0{%- elif states.fan.tasmota.state == 'on' -%}4{%- endif %}
    +      {% endif %}
    +    speed_value_template: "{{ value_json.FanSpeed }}"
    +    availability_topic: tele/tasmota/LWT
    +    payload_off: "0"
    +    payload_on: "4"
    +    payload_low_speed: "1"
    +    payload_medium_speed: "2"
    +    payload_high_speed: "3"
    +    payload_available: "Online"
    +    payload_not_available: "Offline"
    +    speeds:
    +      - "off"
    +      - "low"
    +      - "medium"
    +      - "high"
    +

    Covers~

    Add in Home Assistant using the MQTT Cover integration.

    TuyaMCU Curtain/Shade Motor

    Requires SetOption66 1. In this example dpId1 is for open/close/stop of the motor, dpId2 sets position and dpId3 displays the current position.

    # Example configuration.yaml entry
    +mqtt:
    +  cover:
    +    - unique_id: tuya_curtain
    +      name: "Tuya Curtain"
    +      command_topic: "cmnd/tasmota/TuyaSend4"
    +      payload_open: "1,0"
    +      payload_close: "1,2"
    +      payload_stop: "1,1"
    +      position_open: 0
    +      position_closed: 100
    +      position_topic: "tele/tasmota/RESULT"
    +      position_template: >-
    +            {% if value_json.TuyaReceived.DpType2Id3 is defined %}
    +            {{ value_json.TuyaReceived.DpType2Id3 }}
    +            {% else %}  
    +            {{ state_attr('cover.tuya_curtain','current_position') | int }}
    +            {% endif %}  
    +      set_position_topic: "cmnd/tasmota/TuyaSend2"
    +      set_position_template: '2,{{ position }}'
    +      availability_topic: "tele/tasmota/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +

    If you change name: make sure to reflect that change in the value_template cover name!

    Climate~

    Detailed guide when using MQTT Climate or Generic Thermostat in Home Assistant

    Device Specific~

    Covers

    For shutter position to persist in Home Assistant through device reboots, execute PowerRetain 1.

    These sample configurations should allow the shutter work in Home Assistant. This is only an example and may need further modification to work in your environment.

    This example uses a new configuration for roller shutters with options for positioning. It assumes that %prefix%/%topic%/ is configured in the Tasmota Full Topic MQTT parameter.

    mqtt:
    +  cover:
    +    - name: "Balcony Blinds"
    +      availability_topic: "tele/%topic%/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +      position_topic: stat/%topic%/Shutter1
    +      position_open: 100
    +      position_closed: 0
    +      set_position_topic: "cmnd/%topic%/ShutterPosition1"
    +      command_topic: "cmnd/%topic%/Backlog"
    +      payload_open: "ShutterOpen1"
    +      payload_close: "ShutterClose1"
    +      payload_stop: "ShutterStop1"
    +      retain: false
    +      optimistic: false
    +      qos: 1
    +
    Check Issue 130 for more information about this configuration.

    Another integration example:

    mqtt:
    +  cover:
    +    - name: "Test"
    +      availability_topic: "tele/%topic%/LWT"
    +      state_topic: "stat/%topic%/RESULT"
    +      command_topic: "cmnd/%topic%/Backlog"
    +      value_template: '{{ value | int }}'
    +      qos: 1
    +      retain: false
    +      payload_open: "ShutterOpen1"
    +      payload_close: "ShutterClose1"
    +      payload_stop: "ShutterStop1"
    +      state_open: "ON"
    +      state_closed: "OFF"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +      optimistic: false
    +      tilt_command_topic: 'cmnd/%topic%/ShutterPosition1'
    +      tilt_status_topic: 'cmnd/%topic%/ShutterPosition1'
    +      set_position_topic: 'cmnd/%topic%/ShutterPosition1'
    +      position_topic: "stat/%topic%/SHUTTER1"
    +      tilt_min: 0
    +      tilt_max: 100
    +      tilt_closed_value: 0
    +      tilt_opened_value: 100
    +
    Integration example with position updated during movement (Tasmota versions >= v8.1.0.5):

    mqtt:
    +  cover:
    +    - name: "Balcony Blinds"
    +      availability_topic: "tele/%topic%/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +      position_topic: "stat/%topic%/RESULT"
    +      position_template: >
    +        {% if ('Shutter1' in value_json) and ('Position' in value_json.Shutter1) %}
    +          {{ value_json.Shutter1.Position }}
    +        {% else %}
    +          {% if is_state('cover.balcony_blinds', 'unknown') %}
    +            50
    +          {% else %}
    +            {{ state_attr('cover.balcony_blinds','current_position') }}
    +          {% endif %}
    +        {% endif %}    
    +      position_open: 100
    +      position_closed: 0
    +      set_position_topic: "cmnd/%topic%/ShutterPosition1"
    +      command_topic: "cmnd/%topic%/Backlog"
    +      payload_open: "ShutterOpen1"
    +      payload_close: "ShutterClose1"
    +      payload_stop: "ShutterStop1"
    +

    In addition, add to your home assistant start up automation a query for the current shutter position:

    - alias: "Power state on HA start-up"
    +  trigger:
    +    platform: homeassistant
    +    event: start
    +  action:
    +    - service: mqtt.publish
    +      data:
    +        topic: "cmnd/%shutters grouptopic%/shutterposition"
    +        payload: ""       
    +

    Zigbee Devices~

    Warning

    The following Zigbee examples assume SetOption83, SetOption89, SetOption100, SetOption112, SetOption118, SetOption119 are all set to 0. Setting any of these to 1 will change topic and/or payload structure from that used in the examples.

    Dimmable Light

    This configuration is for a dimmable light reporting on 0xE1F9 using endpoint 1, cluster 8 for brightness. ZbRead part in the template is needed to always update the brightness values.

    # Example configuration.yaml entry
    +mqtt:
    +  light:
    +    - schema: template
    +      name: "Fire Light"
    +      command_topic: "cmnd/zigbee-gateway/Backlog"
    +      state_topic: "tele/zigbee-gateway/SENSOR"
    +      command_on_template: >
    +          {%- if brightness is defined -%}
    +          ZbSend { "device":"0xE1F9", "send":{"Dimmer":{{ brightness }} } }; ZbSend { "device":"0xE1F9", "send":{"Power":true} }; delay 20; ZbRead { "device":"0xE1F9", "endpoint":1, "cluster":8, "read":0 }
    +          {%- else -%}
    +          ZbSend { "device":"0xE1F9", "send":{"Power":true} }; delay 20; ZbRead { "device":"0xE1F9", "endpoint":1, "cluster":8, "read":0 }
    +          {%- endif -%}
    +      command_off_template: 'ZbSend { "device":"0xE1F9", "send":{"Power":false} }; delay 20; ZbRead { "device":"0xE1F9", "endpoint":1, "cluster":8, "read":0 }'
    +      state_template: >
    +          {% if value_json.ZbReceived is defined and value_json.ZbReceived['0xE1F9'] is defined and value_json.ZbReceived['0xE1F9'].Power is defined %}
    +          {% if value_json.ZbReceived['0xE1F9'].Power == true %}
    +          on
    +          {% else %}
    +          off
    +          {% endif %}
    +          {% else %}
    +          {{ states('light.fire_light') }}
    +          {% endif %}
    +      brightness_template: >
    +          {%- if value_json.ZbReceived is defined and value_json.ZbReceived['0xE1F9'] is defined and value_json.ZbReceived['0xE1F9'].Dimmer is defined -%}
    +          {{ value_json['ZbReceived']['0xE1F9'].Dimmer | int }}
    +          {%- else -%}
    +          {{ state_attr('light.fire_light', 'brightness') | int }}
    +          {%- endif -%}
    +

    Water Leak Sensor

    This specific configuration is for Xiaomi Aqara Water Leak sensor reporting on 0x099F.

    # Example configuration.yaml entry
    +mqtt:
    +  binary_sensor:
    +    - name: "Water Leak"
    +      state_topic: "tele/zigbee-gateway/SENSOR"
    +      value_template: >
    +        {%- if value_json.ZbReceived is defined and value_json.ZbReceived['0x099F'] is defined -%}
    +        {%- if value_json.ZbReceived['0x099F']['0500!00'] == '010000FF0000' -%}
    +        ON
    +        {% else %}
    +        OFF
    +        {% endif %}
    +        {% else %}
    +        {{ states('binary_sensor.water_leak') }}
    +        {% endif %}
    +      availability_topic: "tele/zigbee-gateway/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +      device_class: moisture
    +

    Enable join button

    mqtt:
    +  button:
    +    - unique_id: z2t_enable_join
    +      name: "Zigbee2Tasmota enable join"
    +      command_topic: "cmnd/zigbee-gateway/ZbPermitJoin" 
    +      payload_press: "1"
    +      availability_topic: "tele/zigbee-gateway/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +      entity_category: "default"
    +      device_class: "restart"
    +

    Useful Automations~

    Extended device information

    Wi-Fi signal quality can be added as a parameter to any previous configuration by appending this section to the existing configuration yaml

        json_attributes_topic: "tele/tasmota/STATE"
    +    json_attributes_template: "{{ value_json.Wifi | tojson }}"
    +

    Sync Power State

    When MQTT broker or Home Assistant is restarted, or there is a WiFi outage, Tasmota device states may not be synced with Home Assistant. Use this automation to keep your devices in sync, including power state, immediately after Home Assistant is started.

    automation:
    +  - id: Sync Tasmota states
    +    alias: Sync Tasmota states
    +    initial_state: true
    +    trigger:
    +      platform: homeassistant
    +      event: start
    +    action:
    +    # sync state for devices with default fulltopics
    +    - service: mqtt.publish
    +      data:
    +        topic: cmnd/tasmotas/state
    +        payload: ''
    +    # sync state for pre8.2 autodiscovery devices
    +    - service: mqtt.publish
    +      data:
    +        topic: tasmotas/cmnd/state
    +        payload: ''
    +

    To sync a single TuyaMCU device states add this block with your %topic% to the automation. !!! bug You could use tasmotas but SerialSend might cause issues on other devices so proceed with caution.

        - service: mqtt.publish
    +      data:
    +        topic: cmnd/%topic%/serialsend5
    +        payload: 55aa0001000000
    +

    Sync Zigbee device states. Add this block with your %topic% and your Zigbee device name, endpoint and cluster.

    Example for a dimmable light

        - service: mqtt.publish
    +      data:
    +        topic: cmnd/zigbee-gateway/Backlog
    +        payload: ZbRead { "device":"0xE1F9", "endpoint":1, "cluster":6, "read":0 }; delay 5; ZbRead { "device":"0xE1F9", "endpoint":1, "cluster":8, "read":0 }
    +

    Report Firmware Version

    Add a sensor like below for each Tasmota device whose firmware version you want to track.

    mqtt:
    +  sensor:
    +    - platform: mqtt
    +      name: "Tasmota"
    +      state_topic: "stat/tasmota/STATUS2"
    +      value_template: "{{value_json['StatusFWR'].Version }}"
    +      availability_topic: "tele/tasmota/LWT"
    +      payload_available: "Online"
    +      payload_not_available: "Offline"
    +

    Automation to have each device report firmware version on Home Assistant reboot. You can manually trigger this automation from Home Assistant UI.

    automation:
    +  - alias: "Tasmota Firmware Version Check"
    +    trigger:
    +      platform: homeassistant
    +      event: start
    +    action:
    +      - service: mqtt.publish
    +        data:
    +          topic: "cmnd/tasmotas/STATUS"
    +          payload: "2"
    +

    Tip

    If you want all your devices to switch to autodiscovery method go through Developer tools - MQTT by publishing to grouptopic cmnd/tasmotas/SetOption19 with payload 1

    Get most recent Tasmota firmware version number from github

    mqtt:
    +  sensor:
    +    - platform: command_line
    +      name: "Tasmota (latest version)"
    +      command: 'curl -s https://api.github.com/repos/arendst/Tasmota/tags | grep "name" | sort --version-sort -r | head -n 1 | sed -E "s/\s*\"name\": \"(.*)\",*/\1/g"'
    +      scan_interval: 86400 # check once every day
    +

    MQTT Discovery REMOVED~

    Home Assistant MQTT Discovery is removed from all builds of Tasmota and development of this feature is halted!

    Home Assistant has a feature called MQTT discovery. With MQTT discovery no user interaction or configuration file editing is needed to add new devices in Home Assistant. Most of the changes will be updated in HA automatically.

    Enable Autodiscovery~

    Enable autodiscovery on a device with the command:

    SetOption19 1
    +

    After the automatic discovery feature is enabled a retained MQTT message starting with topic "homeassistant/" is sent to the broker. That message contains your device configuration which will be picked up and used by Home Assistant to automatically add your device to MQTT integrations.

    Tasmota uses DeviceName to identify the device in Home Assistant MQTT integration and FriendlyName<x> to identify power outputs (switch or light entities in HA).

    When changing some settings you might need a reboot or use SetOption19 1 again to see the new changes under Home Assistant.

    Special settings for each device type:

    Finalising Setup~

    All automatically discovered entities will show up under Configuration -> Integrations -> MQTT card.

    The entities are grouped under a device defined by DeviceName and identified by Tasmota as the "manufacturer": Integration

    Overview of a Tasmota device in Home Assistant integration compared to Configuration -> Configure Other:

    Device layoutDevice layout

    For every device an informative sensor will be created automatically:

    Informative sensor

    This sensor will update on TelePeriod which is 5 minutes by default

    It will show as "Unavailable" until the first TelePeriod MQTT message

    You can further customise your device in Home Assistant by clicking on the entity name.

    Enabling discovery will automatically change some SetOptions to suit the new configuration

    SetOption4 to 0: Return MQTT response always as RESULT and not as %COMMAND% topic

    SetOption17 to 1: Show Color as a comma-separated decimal string instead of hexadecimal

    SetOption59 to 1: Send tele/%topic%/STATE in addition to stat/%topic%/RESULT for commands State, Power and any command causing a light to be turned on.

    Supported Entities~

    Announced to Home Assistant as Automation Trigger.

    To have buttons discovered SetOption73 must be set to 1 and it will automatically start to listen and publish using /stat/%topic%/BUTTON<x>T topic.

    Discovery will follow all the possible combinations made using SetOption1, SetOption11 and Setoption13.

    Announced to Home Assistant as MQTT Light.

    Discovery supports single channel Dimmer as well as multi-channel RGB, RGBW, RGBCCT and CCT lights.

    To have multi-channel PWM instead of a single light entity under Home Assistant use SetOption68 1.

    If you have a light with 4 or 5 channels (RGB+W or RGB+CCT) you can split them into two separate lights, first one for RGB and second for White/CT.

    Note

    Tasmota will no longer switch %prefix% and %topic% and will keep the default topic structure. This could lead to a very long topic for a light and autodiscovery could fail to parse the necessary code for Home Assistant. In this case a warning will be shown in the logs and Tasmota Console. To avoid this issue keep your Topic and/or FriendlyName as short as possible.

    Alternatively you can configure it manually using Light integration.

    Warning

    Pay attention to the order of the relays when used in conjunction with lights. The relays have priority over the lights, an incorrect order could lead to an erroneous light control. Add them starting from relay1. Entities for relays and lights will not be available in Home Assistant until the configuration will be updated.

    Announced to Home Assistant as MQTT Switch.

    To make a relay discovered as "light" in Home Assistant use command SetOption30 1

    Alternatively you can configure it manually using Light Switch integration.

    Warning

    Pay attention to the order of the relays when used in conjunction with lights. The relays have priority over the lights, an incorrect order could lead to an erroneous light control. Add them starting from relay1. Entities for relays and lights will not be available in Home Assistant until the configuration will be updated.

    Announced to Home Assistant as MQTT Sensor.

    When discovery is enabled Tasmota will send all the sensors information to Home Assistant. For each sensor present, entities will be created in numbers equal to the items present below him.

    Example:
    An AM2301 sensor will generate one entity for Temperature, one for Humidity and one for Dew point.

    Warning

    Please be advised that not all sensors can be correctly rendered under Home Assistant. In those cases a fallback function will be used to create a generic sensor and the correct operation is not guaranteed.

    Announced to Home Assistant as MQTT Binary Sensor and/or as an Automation Trigger.

    To have switches discovered SwitchTopic must be set to a custom name and it will automatically start to listen and publish using stat/%topic%/SWITCH<x> (binary sensor) or stat/%topic%/SWITCH<x>T (trigger) topics.

    Depending by the SwitchModeused, a switch can be a Trigger (TOGGLEor HOLD), a Binary Sensor (ON/OFF) or both at the same time.

    Example:
    When using with SwitchMode 0 Tasmota will create just one Trigger for TOGGLE.
    When using with SwitchMode 1 Tasmota will create a Binary Sensor with ON and OFF Payloads.
    When using with Switchmode 5 Tasmota will create a Binary Sensor with ON and OFF Payloads and a Trigger for TOGGLE.

    All switchmodes are supported with the sole exception of SwitchMode11 and SwitchMode12 able to generate just a TOGGLE trigger.

    When a switch is set to a different topic than 0 is not possible to use Switch#State as a trigger for rules.

    Types of devices not listed above (covers, etc) require manual configuration

    Troubleshooting~

    Test if Tasmota and Home Assistant can communicate properly.

    Open your Home Assistant instance and show your MQTT configuration panel.

    Click on the above button or in your Home Assistant web UI go to Configuration - Integrations, locate MQTT card and click on CONFIGURE. Subscribe to tele/%topic%/STATE and click START LISTENING. You should see a JSON response from your device inside 5 minutes.

    To test control of a relay or light, as Publish a packet topic enter cmnd/%topic%/POWER with payload toggle or 2. When you click PUBLISH your device should switch state and a JSON resc

    \ No newline at end of file diff --git a/HomeSeer/index.html b/HomeSeer/index.html new file mode 100644 index 0000000000..60dbf80cd9 --- /dev/null +++ b/HomeSeer/index.html @@ -0,0 +1 @@ + HomeSeer - Tasmota

    HomeSeer

    For those knowing more about HomeSeer please update this page.

    About HomeSeer HS3 is the industry standard for flexible, powerful, home automation software. A wide selection of software drivers (plug-ins) is available for use with scores of home automation technologies and products.

    The following forum link provides a guide to upload Tasmota to an S20 using SonOTA and integrate it with HomeSeer HS3 using the mcsMQTT plugin.

    Here is a guide for integrating HomeSeer HS3 with the Sonoff 4CH Pro and Tasmota firmware as a Garage Door controller using the mcsMQTT plugin.

    Requirements for HomeSeer HS3 and Tasmota devices: * HomeSeer HS3 * MQTT server * A MQTT plugin for HS3

    Currently there are two plugins, both free: "MQTT" and "mcsMQTT". The former is more intuitive but hasn't been updated for a while, the latter is newer and constantly updated.

    If you use "MQTT" plugin you need to synch the virtual device to reflect the status of the physical button, this can be done with a plugin: * EasyTrigger plugin - costs 25$ (used to synchronise the status of the virtual device in HomeSeer when the Sonoff Tasmota module is operated from the physical button)

    If you use "mcsMQTT", starting from ver 3.0.3+ it allows to create a device that both report and control the status of the Sonoff. More info here: https://forums.homeseer.com/showthread.php?t=192675

    Tasmota Plug-in

    If your interested in a FREE specific plug-in for Sonoff + Tasmota, then Instructions are HERE and you can install it from the Homeseer Plug-ins Manager, in Primary Technology as TasMQTT.

    This plug-in takes care of two-way updates so wall switches update Homseer devices and homeseer changes update Sonoff devices. The plug-in also takes care of monitoring sonoff availability and optionally flags homeseer devices as offline so events can take that into consideration before making changes. When devices come back online Homeseer is updated with the current status of the devices. The plug-in also supports Tasmota Sensors such as the Sonoff TH1 etc. The plug-in has so far been tested extensively with the entire Sonoff Portfolio and as new devices become available we'll test those too.

    This plug-in has been in beta for 6 months and has now reached production. Beta's will continue to be released to support any new Tasmota enabled devices.

    \ No newline at end of file diff --git a/Homebridge/index.html b/Homebridge/index.html new file mode 100644 index 0000000000..2642accb4a --- /dev/null +++ b/Homebridge/index.html @@ -0,0 +1 @@ + Homebridge - Tasmota

    Homebridge

    Homebridge is a NodeJS server which emulates the iOS HomeKit API to enable support for non-native devices (like Sonoff and Magichome devices).

    Tasmota compatible plugins have been developed by macwyznawca and arachnetech as well as others to add support for these devices in Apple's Home app and through iCloud for automation (iPad, HomePod/Apple TV 4 or newer required). Note these plugins are not necessarily specific to Sonoff devices.

    The homebridge-mqttthing package has support for many types of devices and is quite flexible with different options especially for RGB/RGBW LED strips.

    As of 1.0.11 homebridge-mqttthing supports the following Homekit device types:

    • Light bulb
    • Switch
    • Outlet
    • Motion Sensor
    • Occupancy Sensor
    • Light Sensor
    • Contact Sensor
    • Doorbell
    • Security System
    • Smoke Sensor
    • Temperature Sensor
    • Humidity Sensor
    • Stateless Programmable Switch
    • Garage Door
    • Garage Door Opener

    Check out macwyznawca's packages for supported functions, each in a separate package.

    To use Homebridge with Tasmota you need to have an existing MQTT broker set up (see wiki article) and each of your Sonoff devices should have a unique topic set.

    Example screenshot from Apple Home.app

    Home iOS app HomeKit example

    \ No newline at end of file diff --git a/Honeywell-HIH/index.html b/Honeywell-HIH/index.html new file mode 100644 index 0000000000..65eabb9a64 --- /dev/null +++ b/Honeywell-HIH/index.html @@ -0,0 +1,4 @@ + Honeywell HIH temperature and humidity sensor - Tasmota
    Skip to content

    Honeywell HIH temperature and humidity sensor~

    This feature is included only in tasmota-sensors and tasmota32 binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_HIH6
    +#define USE_HIH6    // Enable Honeywell HIH (I2C address 0x27) (+0k6)
    +#endif
    +

    Honeywell HumidIcon™ Digital Humidity and Temperature Sensors HIH Series

    Configuration~

    Wiring~

    Get the 4 pin SIP version if possible, as this will be easier to solder than the 8 pin SMD version.

    wiring diagram

    pins

    Looking at the side of the sensor with a filter: * Pin 1 VDD 1. connect via 0.22 uF to ground 1. connect to 3v3 on the ESP * Pin 2 GND 1. connect to GND on the ESP * Pin 3 SCL 1. connect via 2k2 resistor to 3v3 1. connect to TX on the ESP * Pin 4 SDA 1. connect via 2k2 resistor to 3v3 1. connect to RX on the ESP

    Remember to peel off the white sticker over the filter before use.

    I have tested several of these without the 0.22 uF capacitor and without the pull-up resistors, and I haven't seen any difference in the readings. The cap is probably more important that the resistors, since the internal pull-ups in the ESP8266 will work. That said, if I was installing these in a hard-to-service location, I would follow the wiring diagram exactly.

    I have noticed that temperature readings tend to be a degree or so high if the sensor is wired directly to the ESP: directly connected - don't do this!

    Soldering a 1-2" piece of wire between the ESP and the HIH7121 fixes this issue: connect via a short wire

    Tasmota Settings~

    In the Configuration -> Configure Module page assign: 1. GPIO RX to I2C SDA 2. GPIO TX to I2C SCL

    After a reboot the driver will detect the HIHxxxx automatically and display sensor data.

    <--- tasmota setup

    if you have an LCD display also configured, run I2CDriver3 0

    More documentation on this is in the I2CDEVICES.md file.

    Why the Honeywell HIH series sensors?~

    The go-to humidity sensor for the DIY Arduino/ESP8266 community is the DHT22. Unfortunately, this sensor has a tendency to collect condensation and give false readings (usually 99.9% RH) in wet environments like a bathroom or outside. Not being able to use a humidity sensor in a wet area (IMHO) defeats the point of a humidity sensor, so I set about looking for alternatives.

    After looking at several options, I found the Honeywell HIHxxxx sensors. Different models of the HIH line have different features and work over different ranges of humidity, but most of them use the same I2C protocol, which is now supported in Tasmota. Check the datasheets to compare sensors. One of the most critical features to look for is a hydrophobic filter which allows humid air through, but blocks water droplets from entering the sensor. This is listed on the datasheets as "with filter,condensation resistant". Also keep in mind the humidity range you'll be measuring.

    For my purposes (bathroom humidity sensor), there was a clear choice in the HIH7121-021 (datasheet, non-affiliate direct link to product on Mouser, which works over a range of 0-100% RH, supports I2C, is accurate to within 3%, and has a hydrophobic filter. The article focuses on that sensor, but I suspect much of the information will be applicable to other HIHxxxx sensors.

    by @thepotoo

    \ No newline at end of file diff --git a/How-to-Expand-Sonoff-Basic-to-4MB-Flash/index.html b/How-to-Expand-Sonoff-Basic-to-4MB-Flash/index.html new file mode 100644 index 0000000000..5929fb688f --- /dev/null +++ b/How-to-Expand-Sonoff-Basic-to-4MB-Flash/index.html @@ -0,0 +1,4 @@ + How to Expand Sonoff Basic to 4MB Flash - Tasmota
    Skip to content

    How to Expand Sonoff Basic to 4MB Flash

    Heya,

    In this memo I'll describe how to expand the flash of a sonoff basic from 1MB to 4MB. The advantage for this change is, to avoid to double flash with the minimal and then with the normal firmware and to use more features like Webserver and SSL encryption.

    It was hard to find those information, I'll give credits to some users from the german CreationX Forum.

    Because to unsolder the flash isn't that easy as just solder in pins, this is only for advanced users.

    Don't forget, it's at your own risk and the device could be damaged permanently.

    The Hardware~

    • Soldering iron
    • Tin
    • Tweezers

    The Flash~

    The origin flash block is a XTX PN25F08B 1725XDG. We exchange it with a WINBOND W25Q32FVSIG 1416. You can find the WINBOND Flash on eBay (another ebay) or AliExpress

    winbond1

    winbond-different

    Those China flash memory ICs are cheap productions, means the chance to solder in a new defect not working flash block is high! Be ready to re-do the operation if the upload fails.

    If you want to test the flash block, build yourself a test module, something like this for easy soldering in a flash and test it before soldering it in another sonoff basics board. flash test

    The Operation~

    You will notice that the LED is in the way. Get a pen and mark on of its Pin with the board, so you can solder it in back later in the correct Position. It's a Red-Green LED, if you solder it in the wrong way, it will light up in red.

    After the LED is out of the way, you give some tin on the pins from the flash block and connect all 4 pins in a row together with tin. This way it will be easier to desolder it, because you need to melt all pins at the same time.

    Now get a thin object between the flash and the board, try to gently lift the flash up and at the same time melt both rows of the flash pins tin rotatory. If you got enough space between the board and the flash change to the tweezers and finally take the flash block off.

    Now refresh the contacts on the board for the new flash block with a lil bit of fresh tin. Don't forget to give a lil bit tin to the new pins of the flash block, too!

    Now put it on the contacts on the board and try to lock it with the tweezers. Next take the solder iron and head just one pin and try to arrange the flashblock in the center now. Finally, heat and solder the other pins to the board.

    enter image description here

    You can also use a hot air rework station, this would be much safer, but also more expensive (Amazon). Be sure to protect the rest of the board if using the heat gun! enter image description here

    Good job, you are done :)

    Flashing~

    The next step is to tell Atom (i did not do it with Arduino IDE, sry) to use the whole new 4MB Flash. Note: You can only use 3MB, because 1MB is needed for SPIFFS.

    To do this, you need to edit the platformio.ini file. Search for your preferred language Block and copy the whole Block. Rename it to e.g. Sonoff-DE-4M Now change the following line

    build_flags = -Wl,-Tesp8266.flash.1m0.ld -DMY_LANGUAGE=de-DE
    +

    to

    build_flags = -Wl,-Tesp8266.flash.4m1m.ld -DMY_LANGUAGE=de-DE
    +

    Now scroll back to the top and enter a new default environment with the name of your newly created block

    env_default = sonoff-DE-4M
    +

    You are done with the preparations, connect the Sonoff with the 4MB Flash in flash mode (hold button while powering it up), click clean, click build, click upload. config1enter image description here

    Result~

    On the Information Page on the tasmota web interface you can now see the 4MB flash :) info1 info

    \ No newline at end of file diff --git a/I2CDEVICES/index.html b/I2CDEVICES/index.html new file mode 100644 index 0000000000..0174e0d3d0 --- /dev/null +++ b/I2CDEVICES/index.html @@ -0,0 +1 @@ + I2C Devices - Tasmota
    Skip to content

    I2C devices~

    Tasmota supports several I2C devices. To use them I2C and the device need to be enabled at compile time. I2C and some devices are supported also in the official releases. Devices can be de/-actived on runtime to solve possible address conflicts. (e.g. address 0x27 is used by multiple devices)

    Using command I2cDriver individual drivers can be enabled or disabled at runtime allowing duplicate I2C addresses at compile time. Use the Index from the table below to control I2C drivers like I2cDriver10 0 for disabling BMP support.

    Supported I2C devices~

    The following table lists the supported I2C devices

    Index Define Driver Device Address(es) Description
    1 USE_PCA9685 xdrv_15 PCA9685 0x40 - 0x47 16-channel 12-bit pwm driver
    2 USE_PCF8574 xdrv_28 PCF8574 0x20 - 0x26 8-bit I/O expander (address range overridable)
    2 USE_PCF8574 xdrv_28 PCF8574A 0x39 - 0x3F 8-bit I/O expander (address range overridable)
    3 USE_DISPLAY_LCD xdsp_01 0x27, 0x3F LCD display
    4 USE_DISPLAY_SSD1306 xdsp_02 SSD1306 0x3C - 0x3D Oled display
    5 USE_DISPLAY_MATRIX xdsp_03 HT16K33 0x70 - 0x77 8x8 led matrix
    6 USE_DISPLAY_SH1106 xdsp_07 SH1106 0x3C - 0x3D Oled display
    7 USE_ADE7953 xnrg_07 ADE7953 0x38 Energy monitor
    8 USE_SHT xsns_07 SHT1X Any Temperature and Humidity sensor
    9 USE_HTU xsns_08 HTU21 0x40 Temperature and Humidity sensor
    9 USE_HTU xsns_08 SI7013 0x40 Temperature and Humidity sensor
    9 USE_HTU xsns_08 SI7020 0x40 Temperature and Humidity sensor
    9 USE_HTU xsns_08 SI7021 0x40 Temperature and Humidity sensor
    10 USE_BMP xsns_09 BMP085 0x76 - 0x77 Pressure and temperature sensor
    10 USE_BMP xsns_09 BMP180 0x76 - 0x77 Pressure and temperature sensor
    10 USE_BMP xsns_09 BMP280 0x76 - 0x77 Pressure and temperature sensor
    10 USE_BMP xsns_09 BME280 0x76 - 0x77 Pressure, temperature and humidity sensor
    10 USE_BMP xsns_09 BME680 0x76 - 0x77 Pressure, temperature, humidity and gas sensor
    11 USE_BH1750 xsns_10 BH1750 0x23, 0x5C Ambient light intensity sensor
    12 USE_VEML6070 xsns_11 VEML6070 0x38 - 0x39 Ultra violet light intensity sensor
    13 USE_ADS1115 xsns_12 ADS1115 0x48 - 0x4B 4-channel 16-bit A/D converter
    14 USE_INA219 xsns_13 INA219 0x40 - 0x41, 0x44 - 0x45 Low voltage current sensor
    15 USE_SHT3X xsns_14 SHT3X 0x44 - 0x45 Temperature and Humidity sensor
    15 USE_SHT3X xsns_14 SHT4X 0x44 - 0x45 Temperature and Humidity sensor
    15 USE_SHT3X xsns_14 SHTCX 0x70 Temperature and Humidity sensor
    16 USE_TSL2561 xsns_16 TSL2561 0x29, 0x39, 0x49 Light intensity sensor
    17 USE_MGS xsns_19 Grove 0x04 Multichannel gas sensor
    18 USE_SGP30 xsns_21 SGP30 0x58 Gas (TVOC) and air quality sensor
    19 USE_SI1145 xsns_24 SI1145 0x60 Ultra violet index and light sensor
    19 USE_SI1145 xsns_24 SI1146 0x60 Ultra violet index and light sensor
    19 USE_SI1145 xsns_24 SI1147 0x60 Ultra violet index and light sensor
    20 USE_LM75AD xsns_26 LM75AD 0x48 - 0x4F Temperature sensor
    21 USE_APDS9960 xsns_27 APDS9960 0x39 Proximity ambient light RGB and gesture sensor
    22 USE_MCP230xx xsns_29 MCP23008 0x20 - 0x26 8-bit I/O expander
    22 USE_MCP230xx xsns_29 MCP23017 0x20 - 0x26 16-bit I/O expander
    23 USE_MPR121 xsns_30 MPR121 0x5A - 0x5D Proximity capacitive touch sensor
    24 USE_CCS811 xsns_31 CCS811 0x5A Gas (TVOC) and air quality sensor
    24' USE_CCS811_V2 xsns_31 CCS811 0x5A - 0x5B Gas (TVOC) and air quality sensor
    25 USE_MPU6050 xsns_32 MPU6050 0x68 - 0x69 3-axis gyroscope and temperature sensor
    26 USE_DS3231 xsns_33 DS3231 0x68 Real time clock
    27 USE_MGC3130 xsns_36 MGC3130 0x42 Electric field sensor
    28 USE_MAX44009 xsns_41 MAX44009 0x4A - 0x4B Ambient light intensity sensor
    29 USE_SCD30 xsns_42 SCD30 0x61 CO2 sensor
    30 USE_SPS30 xsns_44 SPS30 0x69 Particle sensor
    31 USE_VL53L0X xsns_45 VL53L0X 0x29 Time-of-flight (ToF) distance sensor
    32 USE_MLX90614 xsns_46 MLX90614 0x5A Infra red temperature sensor
    33 USE_CHIRP xsns_48 CHIRP 0x20 Soil moisture sensor
    34 USE_PAJ7620 xsns_50 PAJ7620 0x73 Gesture sensor
    35 USE_INA226 xsns_54 INA226 0x40 - 0x41, 0x44 - 0x45 Low voltage current sensor
    36 USE_HIH6 xsns_55 HIH6130 0x27 Temperature and Humidity sensor
    37 USE_24C256 xdrv_10 24C256 0x50 Scripter EEPROM storage
    38 USE_DISPLAY_ILI9488 xdsp_08 FT6236 0x38 Touch panel controller
    39 USE_DISPLAY_RA8876 xdsp_10 FT5316 0x38 Touch panel controller
    40 USE_TSL2591 xsns_57 TSL2591 0x29 Light intensity sensor
    41 USE_DHT12 xsns_58 DHT12 0x5C Temperature and humidity sensor
    42 USE_DS1624 xsns_59 DS1621 0x48 - 0x4F Temperature sensor
    42 USE_DS1624 xsns_59 DS1624 0x48 - 0x4F Temperature sensor
    43 USE_AHT1x xsns_63 AHT10/15 0x38 - 0x39 Temperature and humidity sensor
    43 USE_AHT2x xsns_63 AHT20 0x38 Temperature and humidity sensor
    43 USE_AHT2x xsns_63 AM2301B 0x38 Temperature and humidity sensor
    44 USE_WEMOS_MOTOR_V1 xdrv_34 0x2D - 0x30 WEMOS motor shield v1.0.0 (6612FNG)
    45 USE_HDC1080 xsns_65 HDC1080 0x40 Temperature and Humidity sensor
    46 USE_IAQ xsns_66 IAQ 0x5a Air quality sensor
    47 USE_DISPLAY_SEVENSEG xdsp_11 HT16K33 0x70 - 0x77 Seven segment LED
    48 USE_AS3935 xsns_67 AS3935 0x03 Franklin Lightning Sensor
    49 USE_VEML6075 xsns_70 VEML6075 0x10 UVA/UVB/UVINDEX Sensor
    50 USE_VEML7700 xsns_71 VEML7700 0x10 Ambient light intensity sensor
    51 USE_MCP9808 xsns_72 MCP9808 0x18 - 0x1F Temperature sensor
    52 USE_HP303B xsns_73 HP303B 0x76 - 0x77 Pressure and temperature sensor
    53 USE_MLX90640 xdrv_43 MLX90640 0x33 IR array temperature sensor
    54 USE_VL53L1X xsns_77 VL53L1X 0x29 Time-of-flight (ToF) distance sensor
    55 USE_EZOPH xsns_78 EZOPH 0x61 - 0x70 pH sensor
    55 USE_EZOORP xsns_78 EZOORP 0x61 - 0x70 ORP sensor
    55 USE_EZORTD xsns_78 EZORTD 0x61 - 0x70 Temperature sensor
    55 USE_EZOHUM xsns_78 EZOHUM 0x61 - 0x70 Humidity sensor
    55 USE_EZOEC xsns_78 EZOEC 0x61 - 0x70 Electric conductivity sensor
    55 USE_EZOCO2 xsns_78 EZOCO2 0x61 - 0x70 CO2 sensor
    55 USE_EZOO2 xsns_78 EZOO2 0x61 - 0x70 O2 sensor
    55 USE_EZOPRS xsns_78 EZOPRS 0x61 - 0x70 Pressure sensor
    55 USE_EZOFLO xsns_78 EZOFLO 0x61 - 0x70 Flow meter sensor
    55 USE_EZODO xsns_78 EZODO 0x61 - 0x70 Dissolved Oxygen sensor
    55 USE_EZORGB xsns_78 EZORGB 0x61 - 0x70 Color sensor
    55 USE_EZOPMP xsns_78 EZOPMP 0x61 - 0x70 Peristaltic Pump
    56 USE_SEESAW_SOIL xsns_81 SEESOIL 0x36 - 0x39 Adafruit seesaw soil moisture sensor
    57 USE_TOF10120 xsns_84 TOF10120 0x52 Time-of-flight (ToF) distance sensor
    58 USE_MPU_ACCEL xsns_85 MPU_ACCEL 0x68 MPU6886/MPU9250 6-axis MotionTracking sensor from M5Stac k
    59 USE_BM8563 xdrv_56 BM8563 0x51 BM8563 RTC from M5Stack
    60 USE_AM2320 xsns_88 AM2320 0x5C Temperature and Humidity sensor
    61 USE_T67XX xsns_89 T67XX 0x15 CO2 sensor
    62 USE_SCD40 xsns_92 SCD40 0x62 CO2 sensor Sensirion SCD40/SCD41
    63 USE_HM330X xsns_93 HM330X 0x40 Particule sensor
    64 USE_HDC2010 xsns_94 HDC2010 0x40 Temperature and Humidity sensor
    65 USE_ADE7880 xnrg_23 ADE7880 0x38 Energy monitor
    66 USE_PCF85363 xsns_99 PCF85363 0x51 Real time clock
    67 USE_DS3502 xdrv_61 DS3502 0x28 - 0x2B Digital potentiometer
    68 USE_HYT xsns_97 HYTxxx 0x28 Temperature and Humidity sensor
    69 USE_SGP40 xsns_98 SGP40 0x59 Gas (TVOC) and air quality
    70 USE_LUXV30B xsns_99 LUXV30B 0x4A DFRobot SEN0390 V30B lux sensor
    71 USE_QMC5883L xsns_33 QMC5883L 0x0D Magnetic Field Sensor
    72 USE_INA3221 xsns_100 INA3221 0x40-0x43 3-channels Voltage and Current sensor
    73 USE_HMC5883L xsns_101 HMC5883L 0x1E 3-channels Magnetic Field Sensor
    74 USE_DISPLAY_TM1650 xdsp_20 TM1650 0x24 - 0x27, 0x34 - 0x37 Four-digit seven-segment LED controller
    75 USE_PCA9632 xdrv_64 PCA9632 0x60 4-channel 4-bit pwm driver
    76 USE_SEN5X xsns_103 SEN5X 0x69 Gas (VOC/NOx index) and air quality (PPM <1,<2.5,<4,<10)
    \ No newline at end of file diff --git a/I2S-Audio/index.html b/I2S-Audio/index.html new file mode 100644 index 0000000000..309f604c90 --- /dev/null +++ b/I2S-Audio/index.html @@ -0,0 +1,29 @@ + I2S Audio - Tasmota
    Skip to content

    I2S Audio~

    This feature is not included in precompiled binaries

    When compiling your build add the following to user_config_override.h: To use it you must compile your build. Add the following to user_config_override.h:

    #ifndef USE_I2S_AUDIO
    +#define USE_I2S_AUDIO                             // Add support for I2S audio output
    +// #define USE_I2S_NO_DAC                         // Add support for transistor-based output without DAC
    +// #define USE_I2S_LSB                            // Add support for LSBJ chips, e.g. TM8211/PT8211
    +// #define USE_I2S_WEBRADIO                       // Add support for MP3 web radio streaming (only on ESP32 with PSRAM)
    +// #define USE_I2S_SAY_TIME                       // Add support for english speaking clock
    +// #define USE_I2S_RTTTL                          // Add support for Rtttl playback
    +#endif
    +
    +// USE_M5STACK_CORE2, USE_TTGO_WATCH and ESP32S3_BOX already include I2S_AUDIO
    +
    Also requires lib_extra_dirs = lib/lib_audio, lib/libesp32_audio added to the build environment

    I2S (Inter-IC Sound) is a serial, synchronous communication protocol that is usually used for transmitting audio data between two digital audio devices.

    Audio Output~

    I2S DAC

    For audio output an I2S digital audio decoder (DAC) board is required. It is recommended to use an external DAC

    I2S DAC ESP32 ESP8266 (fixed pins)
    BCLK I2S_BCLK GPIO15
    LRCK/WS I2S_WS GPIO02
    DIN I2S_DOUT GPIO03
    SD NC
    GAIN NC
    VIN 3V3 or 5V 3V3 or 5V
    GND GND GND

    Internal DAC~

    ESP32 has two 8-bit DAC (digital to analog converter) channels, connected to GPIO25 (Channel 1) and GPIO26 (Channel 2).

    Those channels can be driven via the I2S driver when using the “built-in DAC mode” enabled with USE_I2S_NO_DAC

    Commands~

    CMD DAC action
    I2SGain 0..100 = sets the volume of the audio signal
    I2SPlay /file.mp3 = plays a .mp3 audio file from the file system, the systems blocks until sound is played
    +/file.mp3 = plays a .mp3 audio file from the file system, sound is played in a separate task not blocking the system
    I2SRtttl string = play Ring Tones Text Transfer Language (RTTTL) ringtones (requires defined USE_I2S_RTTTL)
    I2SSay text = speaks the text you typed (only English language supported)
    I2STime tells current Tasmota time in English (requires defined USE_I2S_SAY_TIME)
    I2SWr url = starts playing an mp3 radio stream, no blocking (requires defined USE_I2S_WEBRADIO)
    no parameter = stops playing the stream

    Audio Input~

    This feature is not included in precompiled binaries

    When compiling your build add the following to user_config_override.h: To use it you must compile your build. Add the following to user_config_override.h:

    #ifndef USE_I2S_AUDIO
    +#define USE_I2S_AUDIO                    // Add support for I2S audio output (needed even if using only microphone)
    +#define USE_I2S_MIC                      // Add support for I2S microphone
    +//#define MIC_PDM                        // Set microphone as PDM (only on ESP32)
    +//#define MIC_CHANNELS 1                 // 2 = stereo (I2S_CHANNEL_FMT_RIGHT_LEFT), 1 = mono (I2S_CHANNEL_FMT_ONLY_RIGHT)
    +//#define MICSRATE 32000                 // Set sample rate
    +#define USE_SHINE                        // Use MP3 encoding (only on ESP32 with PSRAM)
    +//#define MP3_MIC_STREAM                 // Add support for streaming microphone via http (only on ESP32 with PSRAM)
    +  //#define MP3_STREAM_PORT 81           // Choose MP3 stream port (default = 81)
    +#endif
    +
    +// USE_M5STACK_CORE2, USE_TTGO_WATCH and ESP32S3_BOX already include I2S_AUDIO
    +

    I2S Microphone~

    I2S Microphone

    For microphone input an I2S microphone must be connected.

    I2S Microphone ESP32 ESP8266 (fixed pins)
    SCK I2S_BCLK GPIO13
    WS I2S_WS GPIO14
    SD I2S_DIN GPIO12
    L/R GND GND
    VDD 3.3V 3.3V
    GND GND GND

    If you're using only the microphone without a DAC you still need to set pin I2S_DOUT to an unused GPIO.

    PDM Microphone~

    Pulse density modulation (PDM) microphones are not an I2S or PWM microphone but still have a digital signal. They're used in ESP32-S3-BOX, Seeed Xiao Sense and others.

    Compile Tasmota with MIC_PDM defined.

    Microphone ESP32
    CLK I2S_WS
    DATA I2S_DIN
    L/R GND
    VDD 3.3V
    GND GND
    NC I2S_DOUT
    NC I2S_BCLK

    When using PDM microphones the microphone CLK pin is configured as I2S_WS in Tasmota.

    Commands~

    ESP32 with PSRAM required!

    CMD Action
    I2SMGain 1..50 = sets the gain factor of the microphone
    I2SRec (requires defined USE_SHINE)/file.mp3 = starts recording a .mp3 audio file to the file system, no blocking
    no parameter = stops recording
    -? = shows how many seconds already recorded
    I2SStream (requires defined MP3_MIC_STREAM)
    1 = starts streaming .mp3 server at http://<device_ip>:81/stream.mp3
    1 = stop the stream

    I2S Audio Bridge~

    Starts an UDP audio service to connect 2 ESP32 devices as an audio intercom (an example).

    Needs audio output and microphone on 2 devices (no PSRAM needed)

    #ifndef I2S_BRIDGE
    +#define USE_I2S_AUDIO                       // Add support for I2S audio output
    +#define USE_I2S_MIC                         // Add support for I2S microphone
    +#define I2S_BRIDGE                          // Add support for UDP PCM audio bridge
    +  //#define I2S_BRIDGE_PORT    6970         // Set bridge port (default = 6970)
    +#endif
    +
    CMD bridge action
    I2SBridge ip = sets the IP of the slave device
    0 = stop bridge
    1 = start bridge in read mode
    2 = start bridge in write mode
    3 = start bridge in loopback mode
    4 = set bridge to master
    5 = set bridge to slave
    6 = set microphone to swapped
    7 = set microphone to not swapped
    p<x> = sets the push to talk button where x is the button's GPIO pin number

    If a push to talk button is defined the bridge goes to write mode if the button is pushed and to read mode if the button is released

    \ No newline at end of file diff --git a/IAQ/index.html b/IAQ/index.html new file mode 100644 index 0000000000..2543e94cbd --- /dev/null +++ b/IAQ/index.html @@ -0,0 +1,5 @@ + iAQ-Core indoor air quality sensor - Tasmota
    Skip to content

    iAQ-Core indoor air quality sensor~

    This feature is not included in precompiled binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_IAQ
    +#define USE_IAQ         // [I2cDriver46] Enable iAQ-core air quality sensor (I2C address 0x5a) (+0k6 code)
    +#endif
    +

    The iAQ-core indoor air quality module is a low-cost, ultra compact solution for detecting poor air quality. This module uses micro-machined metal oxide semiconductor (MOS) technology to detect a broad range of VOCs while correlating directly with CO2 and TVOC levels in the room.

    Warning

    All testing and driver programming has been done using iAQ-Core C sensor model. We don't know whether iAQ-Core P will work with this driver.

    Configuration~

    Wiring~

    Follow datasheet for required pull-ups when connecting the bare sensor module directly to ESP8266. Wemos and NodeMCU board include the required pull-up.

    Breakout ESP
    VCC/VIN +3.3VDC
    GND GND
    SCL GPIOy
    SDA GPIOx

    Tasmota Settings~

    In the Configuration -> Configure Module page assign:

    1. GPIOx to I2C SDA
    2. GPIOy to I2C SCL

    After configuring the GPIO's the driver will detect iAQ-Core automatically.

    On every power on sensor will run in Warm up mode for 5 minutes to calibrate. During this webUI shows:

    webUI warmup

    and console displays iAQ: Error 10

    When warm-up is finished, sensor will show readings in web UI:

    webUI readout

    and in MQTT topic (according to TelePeriod):

    {"Time":"2020-01-01T00:00:00","IAQ":{"eCO2":450,"TVOC":125,"Resistance":76827}}
    +

    Usage~

    Typical sensor reading should be around 450 ppm eCO2 and 125 ppb TVOC. Resistance is sensor's gas resistance in Ohm and is the baseline reading from which eCO2 and TVOC are calculated from

    If you see error messages in console, take appropriate action:

    iAQ: Error 1
    During use, a message iAQ: Error 1 will randomly appear. This is normal, it's a skipped reading due to sensor still taking environment data during polling.

    iAQ: Error 80
    If this error persists, the sensor is broken and should be replaced.

    iAQ: Error ff
    General connection error. Check your wiring and if the power supply supplies enough power to the sensor. Run I2CScan to check if the sensor can be found on 0x5a I2C address.

    Breakout Boards~

    Breakout board module

    Where to get~

    iAQ-Core Datasheet

    \ No newline at end of file diff --git a/IP-Symcon/index.html b/IP-Symcon/index.html new file mode 100644 index 0000000000..5e6ba91e33 --- /dev/null +++ b/IP-Symcon/index.html @@ -0,0 +1,3 @@ + IP Symcon - Tasmota
    Skip to content

    IP Symcon

    For users knowing more about integrating Tasmota with IP-Symcon please update this page.

    The Tasmota IP-Symcon interface can be found at https://github.com/Schnittcher/IPS-Tasmota

    There are currently two modules for IP Symcon

    Conditions~

    • Mosquitto Broker
    • MQTT Client - currently a modified version of IPS_MQTT by thomasf68
    • IP-Symcon version 4.1 or higher

    IPS-TasmotaConfigurator~

    With the configurator it is possible to generate automatically the IP Symcon instances for the Tasmota devices.

    IPS-Tasmota~

    With this Module it is possible to map devices that contain standard functions of the Tasmota firmware. For example: Sonoff Switch, Sonoff POW, Sonoff 4CH, Sonoff TH

    IPS-TasmotaLED~

    With the IPS-TasmotaLED module it is possible to map the LED modules that run with the Tasmota firmware. For example: WS2812, AiLight, Sonoff Led, B1, BN-SZ01, H801 and MagicHome

    Installation~

    Github Repositorys in IP Symcon via Core Instances -> Modules -> Add

    IPS-KS-MQTT Client:~

    https://github.com/Schnittcher/IPS-KS-MQTT.git
    +

    IPS-Tasmota:~

    https://github.com/Schnittcher/IPS-Tasmota.git
    +
    \ No newline at end of file diff --git a/IPv6/index.html b/IPv6/index.html new file mode 100644 index 0000000000..b9cc415d8c --- /dev/null +++ b/IPv6/index.html @@ -0,0 +1,24 @@ + IPv6 - Tasmota
    Skip to content

    IPv6~

    IPv6 is supported by default on ESP32; requires a specific build for ESP8266

    Tasmota supports dual-stack IPv4 and IPv6 networks on ESP8266 and ESP32. If you want to know more about IPv6 see the wikipedia IPv6 page. Keep in mind that concepts in IPv6 are very different than IPv4.

    Tasmota supports IPv4 only networks (Legacy) and dual-stack IPv4+IPv6 networks.

    Tasmota does not support IPv6 only networks, and it will yield to a crash after some time (may be fixed in the future).

    Building IPv6 version of Tasmota~

    IPv6 is enabled by default on ESP32 since v12.3.1.1.

    For ESP8266 you need to compile with -DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_HIGHER_BANDWIDTH option in platform.ini instead of -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH, and set -DUSE_IPV6. The code impact is +34.5kB; and you need #define USE_IPV6 in my_user_config.h or in user_config_override.h. See Compiling.

    Network pre-requisites~

    In all cases, Tasmota will enable a Link-Local address on all interfaces (Wifi and Ethernet if present). Link-Local addresses start with fe80:: followed by 64 bits derived from the MAC address of the device. Link-Local addresses are not routable across VLANs nor via the Internet and is used only to communicate with the router and with devices on the same LAN (useful in the Matter protocol).

    IPv6 networks generally support SLAAC (Stateless Address Autoconfiguration, SLAAC). SLAAC allows Tasmota to compute an IPv6 Global Address that is routable across VLANs and possibly accross the internet depending on your router configuration.

    For SLAAC to work, the network size must be at least /64 or larger (i.e. /xx with xx lower or equal to 64). The first 64 bits are set by the network, the last 64 bits are derived from the MAC address in the same way than Link-Local.

    Address type Example of value
    MAC C8:2B:96:B9:0B:50
    IPv4 192.168.x.x
    IPv6 Link-Local fe80::ca2b:96ff:feb9:b50
    IPv6 Global 2a01:cb00:xxxx:xxxx:ca2b:96ff:feb9:b50

    IPv6 console example

    Because of limitations in esp-idf, Tasmota does not support IPv6 Temporary Addresses nor ULA addresses. DHCPv6 support is disabled, because it is not really useful in a home network, and because esp-idf support is limited to state-less mode.

    Features known to work (ESP8266 and ESP32):

    • [x] Allocation of a Link-Local address
    • [x] Allocation of a Global address via SLAAC
    • [x] Works on Wifi and Ethernet
    • [x] Response to ping on Global address
    • [x] Webui listens on Global address
    • [x] MQTT to a IPv6 broker
    • [x] DNS resolution for IPv4 (A) and IPv6 (AAAA) records
    • [x] Ping to IPv6 address (ICMP6)
    • [x] Berry webclient and tcpclient to IPv6 addresses
    • [x] Berry udpclient to IPv6 addresses
    • [ ] Berry udpclient to IPv6 multicast addresses (untested but likely to not work)

    dual-stack DNS~

    If a DNS entry has both a v4 address (A record) and a v6 address (AAAA) record, you can choose which address to use:

    • if no IPv6 Global Address is assigned, only A records are queried
    • If a IPv6 Glbal Address is assigned:
    • SetOption149 0: v4 'A' query is done first, then v6 'AAAA' query if no v4 entry was found
    • SetOption149 1: v6 'AAAA' query is done first, then v4 'A' query if no v6 entry was found

    Note: IPv4 is generally assigned first and IPv6 Global Address takes a few more seconds. Even if SetOption149 1 is selected, first queries are likely to return IPv4 addresses anyways.

    Troubleshooting~

    Enable weblog 3 or seriallog 3 to get more details about IPv6 and DNS resolution.

    Example:

    weblog 3
    +ping www.wikipedia.org
    +
    +14:35:50.335 WIF: DNS resolved 'www.wikipedia.org' (185.15.58.224) in 14 ms
    +14:35:50.338 RSL: RESULT = {"Ping":"Done"}
    +14:35:51.475 RSL: RESULT = {"Ping":{"www.wikipedia.org":{"Reachable":true,"IP":"185.15.58.224","Success":1,"Timeout":0,"MinTime":17,"MaxTime":17,"AvgTime":17}}}
    +
    +SetOption149 1
    +ping www.wikipedia.org
    +
    +14:36:24.205 WIF: DNS resolved 'www.wikipedia.org' (2a02:ec80:600:ed1a::1) in 14 ms
    +14:36:24.210 RSL: RESULT = {"Ping":"Done"}
    +14:36:25.232 RSL: RESULT = {"Ping":{"www.wikipedia.org":{"Reachable":true,"IP":"2a02:ec80:600:ed1a::1","Success":1,"Timeout":0,"MinTime":24,"MaxTime":24,"AvgTime":24}}}
    +

    You can also use Status 5 to know all assigned IP addresses and DNS server entries (st is Wifi STA, en is Ethernet):

    Status 5
    +
    +14:37:09.339 WIF: 'st' IPv4 192.168.2.x
    +14:37:09.350 WIF: 'st' IPv6 fe80::ca2b:96ff:feb9:b50 local
    +14:37:09.351 WIF: 'st' IPv6 2a01:cb00:xxxx:xxxx:ca2b:96ff:feb9:b50 
    +14:37:09.363 WIF: 'lo' IPv4 127.0.0.1
    +14:37:09.364 WIF: 'lo' IPv6 ::1 
    +14:37:09.365 WIF: DNS(0): 192.168.2.1
    +14:37:09.366 WIF: DNS(1): 0.0.0.0
    +[...]
    +
    \ No newline at end of file diff --git a/IR-Remote/index.html b/IR-Remote/index.html new file mode 100644 index 0000000000..f41a689b8c --- /dev/null +++ b/IR-Remote/index.html @@ -0,0 +1,9 @@ + IR Remote - Tasmota
    Skip to content

    IR Remote

    IR Remote driver enables sending IR codes through an IR transmitter and receiving them through an IR receiver via IRremoteESP8266 library.

    Most builds support only the common IR protocols, but tasmota-ir.bin supports almost all protocols available in the IRremoteESP8266 library.

    IR Transmitter~

    Common transmitter modules in the market have a weak IR LED, thus effective transmit distance is really short. If you need range it is best to build your own. For the LED we recommend TSAL6400 for best performance.

    YT-IRTM transmitter/receiver board is serial only and does not work with this driver (it's also limited to only NEC IR protocol).

    Wiring~

    IR w/ 2N222 ESP
    data GPIOx
    + 3.3v/5v
    - GND

    Tasmota~

    In the Configuration -> Configure Module page assign:

    • GPIOx to IRsend (8)

    See IR commands for use.

    IR Receiver~

    IR Receiver is used to capture IR codes. Those codes can be sent using IRSend or used as a rule trigger.

    Example uses the widely available KY-022 breakout board.

    Wiring~

    KY-022 ESP
    S GPIOx
    + 5v
    - GND

    Tasmota~

    In the Configuration -> Configure Module page assign:

    • GPIOx to IRrecv (51)

    On a captured code IR receiver sends a tele/%topic%/RESULT JSON response:

    {
    +  "Time": "2019-01-01T00:00:00",
    +  "IrReceived": {
    +    "Protocol": "NEC",
    +    "Bits": 32,
    +    "Data": "0x00FF00FF"
    +  }
    +}
    +

    Further Reading~

    This covers only the basic IR protocols. Read Complete IR Remote Protocols for more advanced applications.

    IR Send RAW configuration and use

    User maintained codes database for IR

    Linux Infrared Remote Control (LIRC) for more information.

    Example circuit with transmitter and receiver: Example circuit with transmitter and receiver

    \ No newline at end of file diff --git a/IRSend-RAW-Encoding/index.html b/IRSend-RAW-Encoding/index.html new file mode 100644 index 0000000000..f17f76da72 --- /dev/null +++ b/IRSend-RAW-Encoding/index.html @@ -0,0 +1,34 @@ + IRSend RAW Encoding - Tasmota
    Skip to content

    IRSend RAW Encoding

    New IR Raw compact encoding~

    (available since v9.0.0.1)

    We had numerous requests from users to expand the buffer sizes because many IR codes would exceed the MQTT/Web/Serial buffer size. Instead, we changed the IR Raw encoding to shrink the size necessary to encode almost any IR code.

    Before (buffer overflow):

    {"IrReceived":{"Protocol":"PIONEER","Bits":64,"Data":"0xA55A50AFA55A50AF","DataLSB":"0xA55A0AF5A55A0AF5","Repeat":0,"RawData":[8574,4224,574,1558,572,502,570,1562,570,502,572,502,570,1562,570,502,570,1562,572,500,572,1560,572,500,572,1560,572,1560,570,504,568,1560,570,502,572,502,570,1562,570,502,570,1560,570,502,572,500,570,502,572,502,570,1560,570,504,572,1558,572,502,570,1564,568,1562,570,1560,572,1560,572,25258,8574,4222,572,1560,570,502,572,1558,572,502,570,502,572,1558,572,500,570,1560,570,502,570,1560,570,502,570,1560,570,1560,570,504,570,1560,572,502,570,502,570,1560,572,502,570,1560,570,502,570,502,570,502,570,502,570,1560,570,502,570,1560,572,502,570 ...

    Now (no overflow):

    {"IrReceived":{"Protocol":"PIONEER","Bits":64,"Data":"0xA55A50AFA55A50AF","DataLSB":"0xA55A0AF5A55A0AF5","Repeat":0,"RawData":[+8570-4240+550-1580C-510+565-1565F-505Fh+570gFhIdChIgFeFgFgIhFgIhF-525C-1560IhIkI-520ChFhFhFgFhIkIhIgIgIkIkI-25270A-4225IkIhIgIhIhIkFhIkFjCgIhIkIkI-500IkIhIhIkFhIgIl+545hIhIoIgIhIkFhFgIkIgFgI],"RawDataInfo":[135,135,0]}}

    The new format still encodes timings for High/low pulses.

    • First the timings are rounded to the closest 5 microsec value.
    • Instead of using commas, values are prefixed with + if it's a HIGH signal, or - if it's a LOW signal.
    • Each new timing value is assigned a letter starting with 'A'
    • If a timing value matches a previously found value, it is replaced with the letter, in uppercase for a HIGH signal, or lowercase for a LOW signal.

    Ex:

    +8570-4240+550-1580C-510+565-1565F-505Fh
    + \__/ \__/ \_/ \__/C \_/ \_/ \__/F \_/Fh
    +  A    B    C   D     E   F   G     H
    +

    Which translates to:

    +8570-4240+550-1580+550-510+565-1565+565-505+565-505
    +or
    +8570,4240,550,1580,550,510,565,1565,565,505,565,505
    +

    If you need to transform the compact format in the legacy format, you can use the online tool: https://tasmota.hadinger.fr/util

    IRSend for raw IR~

    There are two command syntax forms for sending a remote control code in RAW format with IRsend:

    1. IRSend<x> <frequency>,<rawdata1>,<rawdata2>,..,<rawdataN>

    e.g., IRsend 0,926,844,958,832,1798,868,902,848,900,870,900,852,908,918,958,794,934,874,928,1738,934,856,1764 or IRSend 0,+8570-4240+550-1580C-510+565-1565F-505Fh+570gFhIdChIgFeFgFgIhFgIhF-525C-1560IhIkI-520ChFhFhFgFhIkIhIgIgIkIkI-25270A-4225IkIhIgIhIhIkFhIkFjCgIhIkIkI-500IkIhIhIkFhIgIl+545hIhIoIgIhIkFhFgIkIgFgI

    1. IRSend<x> raw,<frequency>[,<header_mark>,<header_space>,<bit_mark>],<zero_space>[, [<one_multiple>] | <one_space>],<bit_stream>

    Where

    Parameter Description
    <frequency> carrier frequency (default 0 = 38kHz)
    <header_mark>** header mark duration (µs) (optional for some protocols)
    <header_space>** header space duration (µs) (optional for some protocols)
    <bit_mark> bit mark duration (µs) (optional for some protocols)
    <zero_space> zero space duration (µs)
    <one_multiple> used to specify the one space duration if the one space duration is an integral multiple of the zero space duration. This parameter may be omitted if the multiple is 2.

    Use of this parameter is mutually exclusive with the <one_space> parameter
    <one_space> one space duration (µs). Use this parameter if the one space duration is not an integral multiple of <zero_space>

    Use of this parameter is mutually exclusive with the <one_multiple> parameter
    <bit_stream> bit stream data (stream of ones and zeroes)

    ** If header_mark and header_space are specified, the gap will be computed as (header_mark + header_space) * 3 with a high limit of 65ms (65,000µs) to avoid 16 bits overflow. If header_mark and header_space are not specified, the gap will be 40ms (40,000µs).

    This command syntax version makes use of the output of the raw IR decoder from ToniA/Raw-IR-decoder-for-Arduino

    Examples for bitstream command syntax~

    rawirdecode output:~

    Number of symbols: 75
    +Symbols:
    +Hh010101101000111011001110000000001100110000000001100000000000000010001100
    +Bytes:
    +00:  0101|0110 | 6A | 01101010
    +01:  1000|1110 | 71 | 01110001
    +02:  1100|1110 | 73 | 01110011
    +03:  0000|0000 | 00 | 00000000
    +04:  1100|1100 | 33 | 00110011
    +05:  0000|0001 | 80 | 10000000
    +06:  1000|0000 | 01 | 00000001
    +07:  0000|0000 | 00 | 00000000
    +08:  1000|1100 | 31 | 00110001
    +6A,71,73,00,33,80,01,00,31
    +Timings (in us):
    +PAUSE SPACE:  0
    +HEADER MARK:  8620
    +HEADER SPACE: 4260
    +BIT MARK:     544
    +ZERO SPACE:   411
    +ONE SPACE:    1496
    +Decoding known protocols...
    +Unknown protocol
    +Bytecount: 9
    +

    Corresponding command:

    IRSend<x> raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100
    +

    Gree air conditioner Power On~

    {8956, 4560, 546, 1710, 588, 616, 584, 618, 584, 1712, 586, 1712, 588, 614, 586, 614, 586, 618, 586, 1706, 590, 616 , 586, 1708, 590, 616, 588, 610, 590, 612, 590, 612, 590, 618, 584, 618, 584, 616, 586, 614, 588, 612, 590, 612, 590, 1712, 586 , 616, 588, 612, 590, 614, 588, 614, 586, 618, 586, 616, 586, 1710, 588, 614, 586, 1708, 590, 610, 592, 612, 590, 1708, 588, 614 , 588, 20084, 548, 612, 590, 614, 588, 614, 588, 616, 586, 654, 546, 616, 586, 616, 586, 614, 588, 612, 588, 610, 592, 612, 590 , 612, 590, 614, 586, 1712, 586, 616, 586, 612, 588, 614, 588, 614, 586, 616, 586, 612, 590, 614, 588, 610, 590, 616, 586, 614 , 588, 612, 590, 612, 590, 614, 588, 614, 588, 614, 588, 1708, 590, 612, 590, 1708, 590}
    +

    Arrange the data into pairs:

    MARK SPACE BIT
    8956 4560 (header)
    546 1710 1
    588 616 0
    584 618 0
    584 1712 1
    586 1712 1
    588 614 0
    586 614 0
    ...
    590 612 0
    590 614 0
    588 614 0
    588 614 0
    588 1708 1
    590 612 0
    590 1708 1
    590 (ignore)

    The header is always the first pair, the zeroes have the shortest space duration and the ones have the longest space duration.

    • header mark is 8956
    • header space is 4560
    • bit mark, get the average, say 590
    • zero space, say 615
    • one space, say 1710

    Corresponding command:

    IRSend<x> raw,0,8956,4560,590,615,1710,1001100 ... 0000101
    +

    Understanding IR encoding~

    Below are several topics that may help you decoding and understanding what your IR remotes are sending (ex: HVAC) or convert from popular IR formats like PRONTO or Broadlink.

    First, have a look at the excellent article from Elektor IR Remote Control Codes

    Decoding IR codes by hand~

    From discussion with @johan1111 on Discord.

    Let's suppose you have an unsupported HVAL and record the following raw IR codes using Tasmota:

    [3094,3062,3092,4442,576,1652,578,528,576,1650,580,528,576,528,576,1650,...]

    IR messages typically start with a long Mark (IR on), followed by a long Space (IR off). Here we see a double sequence of Mark (3094 µs) - Space (3062 µs) - Mark (3092 µs) - Space (4442 µs).

    The actual bitstream is 576,1652,578,528,576,1650,580,528,576,528,576,1650,....

    You need to take timing by pairs, again the first value is Mark (IR on), the second Space (IR off). The Mark is typically of constant time, and the space will vary from short (~528 µs) for bit 0 to long (~1650 µs) for bit 1.

    So the first bits of the bitstream are: 101001...

    Pioneer IR encoding~

    Pioneer IR encoding is very similar to NEC encoding for the bitstream. When capturing IR codes, they will easily be recognized as NEC codes. But they have subtle differences.

    First, the Frequency if 40KHz for Pioneer vs 38KHz for NEC. The number of IR pulses are the same, so all Pioneer timings are 5% shorter than Nec equivalent. Most Pioneer will tolerate the difference, but some won't. If you have a Pioneer device, prefer the Pioneer encoding.

    Second, Pioneer introduced 64 bits messages vs 32 bits for NEC. Most simple Pioneer commands still use 32 bits, but newer require 64 bits. 64 bits messages are actually sent as 2x 32 bits messages with a very short pause in between.

    Example, Pioneer Vol+ is 32 bits message and must be sent twice. You can either send the same message 0xA55A50AF twice with IRSend2 or send a 64 bits message with twice the same 32 payload: 0xA55A50AFA55A50AF.

    IRSend2 {"Protocol":"PIONEER","Bits":32,"Data":"0xA55A50AF"}

    or

    IRSend {"Protocol":"PIONEER","Bits":64,"Data":"0xA55A50AFA55A50AF"}

    Example 2: Pioneer Stereo:

    IRSend {"Protocol":"PIONEER","Bits":64,"Data":"0xA55AF906A55A03FC"}

    How to convert Pioneer codes to Tasmota~

    Pioneer kindly publishes all IR Codes online on its website.

    Let's take a Pioneer Receiver like VSX-820. Choose the right Excel file and navigate in the corresponding tab.

    Pioneer codes are in short format. For example "TV / SAT" code is A59B+A5CD. You now need to convert these short codes into 64 bits IRRemote8266 codes.

    Steps: 1. Take the first byte: 0xA5 or 0b10100101. Pioneer codes are LSB, but we need MSB. So read backwards all bits to 0b10100101 and back to hex A5. Your first byte is A5. Note: You can see here that 0xA5 is a palindrome. 2. Invert all bits of previous byte: 0b01011010. Your second byte is 5A. 3. Take the second byte 0x9B or 0b10011011. Read backwards all bits giving 0b11011001. Your third byte is D9. 4. Invert all bits from previous byte: 0b00100110. Your fourth byte is 26.

    Do the same with the second sequence A5CD, you should find the following sequence: A55AB3CD.

    The final sequence is:

    IRSend {"Protocol":"PIONEER","Bits":64,"Data":"0xA55AD926A55AB3CD"}

    Converting Pronto IR codes to Tasmota~

    Pronto is a very common and early format to describe raw IR codes. It is fully described in this excellent series of articles The Pronto's IR Code Format

    If you don't want to manually convert the pronto IR codes, you can use the Tasmota-IR-Code-Converter site to do it for you.

    Pronto is commonly represented as a series of 4-digits HEX numbers in unsigned 16-bits format.

    Example, from Foxtel Vol+:

    Vol +
    0000 0073 0000 0012 000F 000A 0006 000A 0006 0016 0006 000A 0006 0010 0006 0016 0006 0016 0006 000A 0006 000A 0006 000A 0006 0016 0006 0010 0006 0016 0006 000A 0006 0010 0006 000A 0006 000A 0006 0CA0

    To convert from Pronto to IRSend by hand it requires extra work.

    1. Ignore first 0000
    2. 0073 is the IR frequency, compute as "Frequency = 1000000/(N * .241246)". So for 0x73 (115), this gives 36KHz. First value for IRSend is 36.
    3. 0000 is the length of the One Time Burst. There is no one time burst
    4. 0012 - Decimal 18 is the length of the repeat burst. There are 18 bits (Burst pairs) in this code. Next pulses are measured in pulses of the IR clock, so it depends on the frequency. With 36KHz, each pulse is (1000/36) 27.7 microseconds So you need to multiply by 27.7 for Tasmota to get actual µs 000F 000A - becomes 415,277 0006 000A - becomes 166,277 And so on...

    In the end, it will look like:

    IRSend 36,415,277,166,277,...

    Broadlink is another popular device to send and receive IR/RF codes. Unfortunately, it does not run ESP8266, so it cannot be Tasmotized.

    Broadlink codes come either in Base64 format or in Hex format. Below will use Hex format which is easier to work by hand.

    Example:

    "Code":
    "2600700000015692171117111612171116121612161117111735173517361636161117351735173517111636161216121611171116121612163616111735173517351735173517351700053D0001554916000C4E0001554916000C4E0001564817000C4D0001564917000C4C0001564917000D050000000000000000",
    "Base64":
    "JgBwAAABVpIXERcRFhIXERYSFhIWERcRFzUXNRc2FjYWERc1FzUXNRcRFjYWEhYSFhEXERYSFhIWNhYRFzUXNRc1FzUXNRc1FwAFPQABVUkWAAxOAAFVSRYADE4AAVZIFwAMTQABVkkXAAxMAAFWSRcADQUAAAAAAAAAAA==

    The Broadling protocol is described here: Broadlink RM2 network protocol

    Here is a copy of the part specifically describing

    Offset Contents
    0x00 0x26 = IR, 0xb2 for RF 433Mhz, 0xd7 for RF 315Mhz
    0x01 repeat count, (0 = no repeat, 1 send twice, .....)
    0x02-0x03 Length of the following data in little endian
    0x04 .... Pulse lengths in 2^-15 s units (µs * 269 / 8192 works very well)
    .... 0x0d 0x05 at the end for IR only

    Each value is represented by one byte. If the length exceeds one byte then it is stored big endian with a leading 0.

    Example: The header for an Optoma projector is 8920 4450
    8920 * 269 / 8192 = 0x124
    4450 * 269 / 8192 = 0x92
    ... which would be encoded as 0x00 0x1 0x24 0x92 in broalink format.

    You have all the needed information to convert from Broadlink to Tasmota...

    See broadlink-ir-converter for a complete implementation example (in JavaScript) which is also available as a package on the NPM registry.

    \ No newline at end of file diff --git a/Integrations/index.html b/Integrations/index.html new file mode 100644 index 0000000000..ab361ac75a --- /dev/null +++ b/Integrations/index.html @@ -0,0 +1 @@ + Smart Home Integrations - Tasmota
    Skip to content

    Introduction~

    Tasmota easily integrates with many home automation solutions reporting status and sensor data and allowing complete control.

    HomeAssistant openHAB Domoticz KNX Alexa Gladys Assistant

    \ No newline at end of file diff --git a/JSON-Status-Responses/index.html b/JSON-Status-Responses/index.html new file mode 100644 index 0000000000..2630066b42 --- /dev/null +++ b/JSON-Status-Responses/index.html @@ -0,0 +1,209 @@ + JSON Status Responses - Tasmota
    Skip to content

    JSON Status Responses

    JSON Changes~

    temp note: for now (6.0.0a) the statetext overwrites the json power result (e.g. "ON" => "AN" or any other statetext the users enter)

    Basic Response~

        {
    +      "Status": {
    +        "Module": 1,
    +        "FriendlyName": "XXX",
    +        "Topic": "sonoff",
    +        "ButtonTopic": "0",
    +        "Power": 0,
    +        "PowerOnState": 0,
    +        "LedState": 1,
    +        "SaveData": 0,
    +        "SaveState": 1,
    +        "ButtonRetain": 0,
    +        "PowerRetain": 0
    +      },
    +      "StatusPRM": {
    +        "Baudrate": 115200,
    +        "GroupTopic": "sonoffs",
    +        "OtaUrl": "XXX",
    +        "Uptime": "1 02:33:26",
    +        "Sleep": 150,
    +        "BootCount": 32,
    +        "SaveCount": 72,
    +        "SaveAddress": "FB000"
    +      },
    +      "StatusFWR": {
    +        "Version": "5.12.0a",
    +        "BuildDateTime": "2018.02.11 16:15:40",
    +        "Boot": 31,
    +        "Core": "2_4_0",
    +        "SDK": "2.1.0(deb1901)"
    +      },
    +      "StatusLOG": {
    +        "SerialLog": 0,
    +        "WebLog": 4,
    +        "SysLog": 0,
    +        "LogHost": "domus1",
    +        "LogPort": 514,
    +        "SSId1": "XXX",
    +        "SSId2": "XXX",
    +        "TelePeriod": 300,
    +        "SetOption": "00000001"
    +      },
    +      "StatusMEM": {
    +        "ProgramSize": 457,
    +        "Free": 544,
    +        "Heap": 23,
    +        "ProgramFlashSize": 1024,
    +        "FlashSize": 1024,
    +        "FlashMode": 3
    +      },
    +      "StatusNET": {
    +        "Hostname": "XXX",
    +        "IPAddress": "192.168.178.XX",
    +        "Gateway": "192.168.178.XX",
    +        "Subnetmask": "255.255.255.XX",
    +        "DNSServer": "192.168.178.XX",
    +        "Mac": "2C:3A:E8:XX:XX:XX",
    +        "Webserver": 2,
    +        "WifiConfig": 4
    +      },
    +      "StatusTIM": {
    +        "UTC": "Thu Feb 15 00:00:50 2018",
    +        "Local": "Thu Feb 15 01:00:50 2018",
    +        "StartDST": "Sun Mar 25 02:00:00 2018",
    +        "EndDST": "Sun Oct 28 03:00:00 2018",
    +        "Timezone": 1
    +      },
    +      "StatusSNS": {
    +        "Time": "2018.02.15 01:00:50",
    +        "Switch1": "OFF"
    +      },
    +      "StatusSTS": {
    +        "Time": "2018.02.15 01:00:50",
    +        "Uptime": "1 02:33:26",
    +        "Vcc": 3.504,
    +        "POWER": "OFF",
    +        "Wifi": {
    +          "AP": 1,
    +          "SSId": "XXX",
    +          "RSSI": 100,
    +          "APMac": "34:31:C4:XX:XX:XX"
    +        }
    +      }
    +    }
    +

    MQTT~

    After StatusNET

        "StatusMQT": {
    +        "MqttHost": "192.168.XXX.XX",
    +        "MqttPort": 1883,
    +        "MqttClientMask": "DVES_%06X",
    +        "MqttClient": "DVES_4AXXXX",
    +        "MqttUser": "admin",
    +        "MAX_PACKET_SIZE": 1000,
    +        "KEEPALIVE": 15
    +      },
    +

    Domoticz~

    idx, nvalue, svalue without array

          ..."StatusTIM": {
    +        "UTC": "Thu Feb 01 20:29:40 2018",
    +        "Local": "Thu Feb 01 21:29:40 2018",
    +        "StartDST": "Sun Mar 25 02:00:00 2018",
    +        "EndDST": "Sun Oct 28 03:00:00 2018",
    +        "Timezone": 1
    +      },
    +      "idx": 286,
    +      "nvalue": 0,
    +      "svalue": "19.7",
    +      "StatusSNS": {
    +        "Time": "2018.02.01 21:29:40",
    +        "DS18B20": {
    +          "Temperature": 19.7
    +        },
    +        "TempUnit": "C"
    +      },
    +      "StatusSTS": { ...
    +

    POW~

    After StatusTIM

        "StatusPTH": {
    +        "PowerLow": 0,
    +        "PowerHigh": 0,
    +        "VoltageLow": 0,
    +        "VoltageHigh": 0,
    +        "CurrentLow": 0,
    +        "CurrentHigh": 0
    +      },
    +      "StatusSNS": {
    +        "Time": "2018.02.04 23:17:01",
    +        "ENERGY": {
    +          "Total": 3.185,
    +          "Yesterday": 3.058,
    +          "Today": 0.127,
    +          "Power": 0,
    +          "Factor": 0.00,
    +          "Voltage": 221,
    +          "Current": 0.000
    +        }
    +      },
    +

    Sensors~

    AM2301~

        "StatusSNS": {
    +        "Time": "2018.02.01 22:52:09",
    +        "AM2301": {
    +          "Temperature": 15.5,
    +          "Humidity": 50.6
    +        },
    +        "TempUnit": "C"
    +      },
    +

    BMP280~

        {
    +      "StatusSNS": {
    +        "Time": "2018-02-10T22:46:34",
    +        "BMP280": {
    +          "Temperature": 80.9,
    +          "Pressure": 984.4
    +        }
    +      }
    +    }
    +

    DHT11~

        "StatusSNS": {
    +        "Time": "2018.02.01 22:48:39",
    +        "DHT11": {
    +          "Temperature": 12.0,
    +          "Humidity": 42.0
    +        },
    +        "TempUnit": "C"
    +      },
    +

    DS18B20~

          "StatusSNS": {
    +        "Time": "2018.02.01 21:29:40",
    +        "DS18B20": {
    +          "Temperature": 19.7
    +        },
    +        "TempUnit": "C"
    +      },
    +

    SHT3X (and DHT11, multiple Sensor example)~

        {
    +      "StatusSNS": {
    +        "Time": "2018-02-07T20:16:19",
    +        "DHT11": {
    +          "Temperature": 78.8,
    +          "Humidity": 27.0
    +        },
    +        "SHT3X": {
    +          "Temperature": 74.8,
    +          "Humidity": 18.9
    +        },
    +        "TempUnit": "F"
    +      }
    +    }
    +

    Sonoff SC~

          "StatusSNS": {
    +        "Time": "2018-02-16T16:18:49",
    +        "Temperature": 25,
    +        "Humidity": 83,
    +        "Light": 10,
    +        "Noise": 20,
    +        "AirQuality": 100,
    +        "TempUnit": "C"
    +      },
    +

    PMS5003 and HTU21~

        "StatusSNS": {
    +        "Time": "2018-02-16T16:22:12",
    +        "HTU21": {
    +          "Temperature": 24.7,
    +          "Humidity": 32.1
    +        },
    +        "PMS5003": {
    +          "CF1": 1,
    +          "CF2.5": 2,
    +          "CF10": 2,
    +          "PM1": 1,
    +          "PM2.5": 2,
    +          "PM10": 2,
    +          "PB0.3": 423,
    +          "PB0.5": 116,
    +          "PB1": 17,
    +          "PB2.5": 1,
    +          "PB5": 0,
    +          "PB10": 0
    +        },
    +        "TempUnit": "C"
    +      },   
    +
    \ No newline at end of file diff --git a/KNX/index.html b/KNX/index.html new file mode 100644 index 0000000000..dd9455e02d --- /dev/null +++ b/KNX/index.html @@ -0,0 +1,8 @@ + KNX - Tasmota
    Skip to content

    KNX

    This feature is included in ESP32 builds, but for ESP8266 it is included only in tasmota-knx build

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_KNX
    +#define USE_KNX         // Enable KNX IP Protocol Support (+9.4k code, +3k7 mem)
    +#endif
    +

    What is KNX?~

    The KNX IP Protocol is an international open standard for smart homes and smart buildings automation. It is a decentralized system. Each device can talk directly to each other without the need of a central controller or server. Any panel or server is just for telesupervision and for sending requests. KNX IP Protocol uses a UDP multicast on 224.0.23.12 : 3671, so there is no need for a KNX Router unless you want to communicate to KNX Devices on different mediums than IP (Twisted Pair, RF, Powerline).

    Each device has a physical address (like a fixed IP) as 1 . 1 . 0. This address is used for configuration purposes and has to be unique in the installation.

    Each device can be configured with group addresses as 2 / 2 / 1 and that address can be used for sending/receiving commands. So, for example, if 2 devices that are configured with the 2 / 2 / 1 for turning on/off their outputs, and other device send Turn ON command to 2 / 2 / 1, both devices will turn on their outputs.

    Integration~

    Several home automation systems have KNX support. For example, Home Assistant has a XKNX Python Library to connect to KNX IP devices. If you don't have a KNX Router, you can use a Software KNX Router like Calimero or KNXd.

    If you use the ETS (KNX Provisioning Software) you can add any TasmotaTasmota KNX as a dummy device.

    If the Tasmotadevice is connecting to a Wifi Repeater you might experience some issues receiving KNX Telegrams. This also applies to mDNS and Emulation features.

    Implemented Features~

    The implemented features, up to now, in KNX for Tasmota are:

    General:

    • buttons (just push)
    • relays (on/off/toggle)
    • lights (led strips, etc. but just on/off)

    Sensor lists that you can use in KNX is (only one sensor per type):

    • Temperature
    • Humidity
    • Energy (v, i, power)

    For using rules:

    • send KNX command (on/off)
    • receive KNX command (on/off)
    • send values by KNX (any float type, temperature for example)
    • receive a KNX read request
    • send and receive SCENE commands

    Usage Examples~

    There are multiple possible configurations. Here are explained just a few as example. The options for selecting relays, buttons, sensors, etc. are only available if were configured on Configure Module Menu.

    To configure KNX, enter on the Configuration Menu of Tasmota and select Configure KNX.

    Note on KNX communication enhancement option: As Wifi Multicast communication is not reliable in some wifi router due to IGMP problems or Snooping, an enhancement was implemented. This option increase the reliability by reducing the chances of losing telegrams, sending the same telegram 3 times. In practice it works really good and it is enough for normal home use. When this option is on, Tasmota will ignore toggle commands by KNX if those are sent more than 1 toggle per second. Just 1 toggle per second is working fine.

    1) Setting Several Tasmota to be controlled as one by a Home Automation System:~

    We can set one of the group address to be the same in all the devices so as to turn them on or off at the same time. In this case, so as to inform the status of all the relays to the Automation System, just one of the devices have to be configured as the responder. If you use the same Group Address for sending and receiving, you have to take into account not to make loops.

    DEVICE 1

    DEVICE 2

    2) Setting 2 Tasmota to be linked as stair lights:~

    We can set one device to send the status of its output and another to read that and follow. And the second device can send the status of its button and the first device will toggle. With this configuration we can avoid to make a loop.

    DEVICE 1

    DEVICE 2

    3) Setting a button as initiator of a scene:~

    Just setting one device to send the push of a button, and the rest just use that value to turn them on. In this case, there is no toggle. Every time the button is pushed, the turn on command is sent.

    DEVICE 1

    DEVICE 2

    4) Setting a Temperature sensor:~

    We can configure to send the value of temperature or humidity every teleperiod. This teleperiod can be configured. See TasmotaTasmota docs. It is recommended also to set the reply temperature address.

    5) Using rules:~

    More functionality can be added to Tasmota using rules.

    • In the KNX Menu, can be set a Group Address to send data or commands by rules, as KNX TX1 to KNX TX5

    In rules we can use the command KnxTx_Cmnd1 1 to send an ON state command to the group address set in KNX TX1 slot of the KNX menu. Also, we can use the command KnxTx_Val1 15 to send a 15 value to the group address set in KNX TX1 slot of the KNX menu.

    • In the KNX Menu can be set a Group Address to receive commands by rules as KNX RX1 to KNX RX5

    In rules we can use the events to catch the reception of COMMANDS from KNX to those RX Slots.

    Example: rule on event#knxrx_cmnd1 do var1 %value% endon to store the command received in the variable VAR1

    In rules we can use the events to catch the reception of VALUES from KNX to those RX Slots.

    Example: rule on event#knxrx_val1 do var1 %value% endon to store the value received in the variable VAR1

    Also, if a Read request is received from KNX Network, we can use that in a rule as for example: rule on event#knxrx_req1 do knxtx_val1 %var3% endon

    NOTE: KnxTX_valn command, KNXRX_Reqn trigger and sensors' telegrams, uses KNX DPT14 (32 bits float) since 9.1.0.2 . Old versions use DPT9 (16 bits float). Old and new versions can not send values between each other. Only commands. It is recommended to have all devices on the same version.

    SCENES

    For using the KNX Scenes Feature, you need to add a rule with the behaviour you want for that scene like:

    Rule1 on EVENT#KNX_SCENE=0 do power1 1 endon on EVENT#KNX_SCENE=1 do power1 0 endon

    6) Rule to send KNX Telegram with BH1750 Sensor Data:~

    • If you want to send your sensor values by KNX every teleperiod time to the Group Address defined in KNX_TX1, you can use the following rule:
    rule1 1
    +rule1 on tele-BH1750#Illuminance do knxtx_val1 %value% endon
    +
    • If you want to send your sensor values by KNX only when it changes in a delta of 10 lx to the Group Address defined in KNX_TX1, you can use the following rule:
    rule1 1
    +rule1 on system#boot do backlog var1 0; var2 0 endon on BH1750#Illuminance>%var1% do backlog var1 %value%; knxtx_val1 %value%; var2 %value%; add1 5; sub2 5 endon on BH1750#Illuminance<%var2% do backlog var2 %value%; knxtx_val1 %value%; var1 %value%; add1 5; sub2 5 endon
    +
    \ No newline at end of file diff --git a/LM75AD/index.html b/LM75AD/index.html new file mode 100644 index 0000000000..62dbb5827f --- /dev/null +++ b/LM75AD/index.html @@ -0,0 +1,4 @@ + LM75AD temperature sensor - Tasmota
    Skip to content

    LM75AD temperature sensor~

    This feature is included only in tasmota-sensors and tasmota32 binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_LM75AD 
    +#define USE_LM75AD      // [I2cDriver20] Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
    +#endif
    +

    The LM75AD is an I2C temperature sensor that converts temperature directly to digital signals from -55°C to +125°C and achieves an accuracy of 0.125°C

    Some features of the LM75A chip not implemented in this driver:
    - Programmable temperature threshold and hysteresis set points - Stand-alone operation as thermostat at power-up

    LM75A Datasheet

    Configuration~

    Wiring~

    LM75AD ESP
    VCC 3.3V
    GND GND
    SDA GPIOx
    SCL GPIOy
    OS not used

    The driver currently only allows the use of a single LM75AD sensor on any of the addresses configurable through pins A0, A1 and A2. These are conveniently broken out on the breakout board either as pins or as solderable joints as can be seen on the example breakout board.

    These pins need to be tied down to either GND or VCC (3.3V) and the resulting I2C address will be set during power-up according to the table: Address MAP

    Tasmota Settings~

    In the Configuration -> Configure Module page assign:

    1. GPIOx to I2C SDA
    2. GPIOy to I2C SCL

    After a reboot the driver will detect LM75AD automatically and display Temperature.

    Commands~

    TempOffset can be used for calibrating the measured temperature. This setting affects all temperature sensors on the device.

    Breakout Boards~

    Can also be used standalone if soldered to a board given the diagram is used

    Circuit

    R1 and R2 are pull-up resistors which are required by the I2C bus to operate properly. If you have other I2C sensors with pull-up resistors connected to the same I2C bus it's probably not necessary to have them.

    \ No newline at end of file diff --git a/LMT01/index.html b/LMT01/index.html new file mode 100644 index 0000000000..dcdc38f3fa --- /dev/null +++ b/LMT01/index.html @@ -0,0 +1,4 @@ + LMT01 temperature sensor - Tasmota
    Skip to content

    LMT01 temperature sensor~

    This feature is included only in tasmota-sensors and tasmota32 binaries

    When compiling your build add the following to user_config_override.h:

    #ifndef USE_LMT01 
    +#define USE_LMT01      // Add support for TI LMT01 temperature sensor, count pulses on single GPIO (+0k5 code)
    +#endif
    +

    The Texas Instruments LMT01 is 2-pin digital output temperature sensor that converts temperature directly to a sequence of digital pulses, reading a range from -50°C to +150°C with up to 0.5°C accuracy. It is available in a DIY-friendly small TO-92 package.

    Connection is by a single GPIO to count the pulses, which needs a driver transistor to convert the small current changes into logic level voltage swings.

    Limitations of current implementation:

    • Only the simple linear conversion is used. The datasheet suggests the use of a look-up table to improve the accuracy at the edges of the range, this is not yet implemented.
    • The sensor code only supports one LMT01 instance.

    Configuration~

    Choose a pin GPIOx. The GPIO can be connected to the LMT01 via a NPN transistor, so current pulses pull the GPIO line low: see Figure 32 in the LMT01 datasheet.

    In the Configuration -> Configure Module page assign:

    • GPIOx to LMT01 Pulse

    After a reboot the sensor will be detected and displayed. If the connection is not working, the pulse counting will timeout and you will see null displayed for the temperature.

    \ No newline at end of file diff --git a/LVGL/index.html b/LVGL/index.html new file mode 100644 index 0000000000..566f9dd978 --- /dev/null +++ b/LVGL/index.html @@ -0,0 +1,230 @@ + LVGL - Tasmota
    Skip to content

    Light and Versatile Embedded Graphics Library ~

    Tasmota now supports openHASP-compatible templates - named HASPmota - which makes it much easier to start with LVGL. Check the HASPmota documentation.

    This feature is included in tasmota32-lvgl.bin

    Supported version: LVGL v8.3.8, LodePNG v20201017, Freetype 2.10.4

    LVGL (Light and Versatile Graphics Library) is Tasmota's next generation display. It is powerful, lightweight and simple to use. It combines:

    • LVGL's powerful graphics and GUI library for embedded
    • Tasmota's stability, richness of features and frugality on resources
    • Berry's powerful language similar to MicroPython

    LVGL is an open-source graphics library providing everything you need to create embedded GUI with easy-to-use graphical elements, beautiful visual effects and low memory footprint.

    Berry is an ultra-lightweight dynamically typed embedded scripting language. It is designed for lower-performance embedded devices

    After compiling Tasmota with LVGL support and configuring uDisplay (see below), you can start using LVGL through the Berry console.

    Using LVGL~

    This example uses the M5Stack Fire device.

    Use the following template to define the relevant SPI GPIOs. Also set one of the unused GPIOs to Option A3.

    The complete configuration for M5Stack Fire is:

    Template {"NAME":"M5stack Fire","GPIO":[6210,1,1,1,1,1,1,1,1,1,768,1,1,1,736,672,0,640,608,704,0,3872,3872,800,0,0,0,0,992,1024,1,0,0,34,33,32],"FLAG":0,"BASE":1,"CMND":"Module 0"}
    +

    Save the following file as display.ini in the file system:

    :H,ILI9341,320,240,16,SPI,1,*,*,*,*,*,*,*,40
    +:B,60,1
    +:I
    +EF,3,03,80,02
    +CF,3,00,C1,30
    +ED,4,64,03,12,81
    +E8,3,85,00,78
    +CB,5,39,2C,00,34,02
    +F7,1,20
    +EA,2,00,00
    +C0,1,23
    +C1,1,10
    +C5,2,3e,28
    +C7,1,86
    +36,1,48
    +37,1,00
    +3A,1,55
    +B1,2,00,18
    +B6,3,08,82,27
    +F2,1,00
    +26,1,01
    +E0,0F,0F,31,2B,0C,0E,08,4E,F1,37,07,10,03,0E,09,00
    +E1,0F,00,0E,14,03,11,07,31,C1,48,08,0F,0C,31,36,0F
    +11,80
    +29,80
    +:o,28
    +:O,29
    +:A,2A,2B,2C
    +:R,36
    +:0,08,00,00,00
    +:1,68,00,00,01
    +:2,C8,00,00,02
    +:3,A8,00,00,03
    +#
    +

    Then open the Berry console and copy/paste the following: (alternatively create an autoexec.be file with this content):

    #- start LVGL and init environment -#
    +lv.start()
    +
    +hres = lv.get_hor_res()       # should be 320
    +vres = lv.get_ver_res()       # should be 240
    +
    +scr = lv.scr_act()            # default screan object
    +f20 = lv.montserrat_font(20)  # load embedded Montserrat 20
    +
    +#- Background with a gradient from black #000000 (bottom) to dark blue #0000A0 (top) -#
    +scr.set_style_bg_color(lv.color(0x0000A0), lv.PART_MAIN | lv.STATE_DEFAULT)
    +scr.set_style_bg_grad_color(lv.color(0x000000), lv.PART_MAIN | lv.STATE_DEFAULT)
    +scr.set_style_bg_grad_dir(lv.GRAD_DIR_VER, lv.PART_MAIN | lv.STATE_DEFAULT)
    +
    +#- Upper state line -#
    +stat_line = lv.label(scr)
    +if f20 != nil stat_line.set_style_text_font(f20, lv.PART_MAIN | lv.STATE_DEFAULT) end
    +stat_line.set_long_mode(lv.LABEL_LONG_SCROLL)                                        # auto scrolling if text does not fit
    +stat_line.set_width(hres)
    +stat_line.set_align(lv.TEXT_ALIGN_LEFT)                                              # align text left
    +stat_line.set_style_bg_color(lv.color(0xD00000), lv.PART_MAIN | lv.STATE_DEFAULT)    # background #000088
    +stat_line.set_style_bg_opa(lv.OPA_COVER, lv.PART_MAIN | lv.STATE_DEFAULT)            # 100% background opacity
    +stat_line.set_style_text_color(lv.color(0xFFFFFF), lv.PART_MAIN | lv.STATE_DEFAULT)  # text color #FFFFFF
    +stat_line.set_text("Tasmota")
    +stat_line.refr_size()                                                                # new in LVGL8
    +stat_line.refr_pos()                                                                 # new in LVGL8
    +
    +#- display wifi strength indicator icon (for professionals ;) -#
    +wifi_icon = lv_wifi_arcs_icon(stat_line)    # the widget takes care of positioning and driver stuff
    +clock_icon = lv_clock_icon(stat_line)
    +
    +#- create a style for the buttons -#
    +btn_style = lv.style()
    +btn_style.set_radius(10)                        # radius of rounded corners
    +btn_style.set_bg_opa(lv.OPA_COVER)              # 100% background opacity
    +if f20 != nil btn_style.set_text_font(f20) end  # set font to Montserrat 20
    +btn_style.set_bg_color(lv.color(0x1fa3ec))      # background color #1FA3EC (Tasmota Blue)
    +btn_style.set_border_color(lv.color(0x0000FF))  # border color #0000FF
    +btn_style.set_text_color(lv.color(0xFFFFFF))    # text color white #FFFFFF
    +
    +#- create buttons -#
    +prev_btn = lv.btn(scr)                            # create button with main screen as parent
    +prev_btn.set_pos(20,vres-40)                      # position of button
    +prev_btn.set_size(80, 30)                         # size of button
    +prev_btn.add_style(btn_style, lv.PART_MAIN | lv.STATE_DEFAULT)   # style of button
    +prev_label = lv.label(prev_btn)                   # create a label as sub-object
    +prev_label.set_text("<")                          # set label text
    +prev_label.center()
    +
    +next_btn = lv.btn(scr)                            # right button
    +next_btn.set_pos(220,vres-40)
    +next_btn.set_size(80, 30)
    +next_btn.add_style(btn_style, lv.PART_MAIN | lv.STATE_DEFAULT)
    +next_label = lv.label(next_btn)
    +next_label.set_text(">")
    +next_label.center()
    +
    +home_btn = lv.btn(scr)                            # center button
    +home_btn.set_pos(120,vres-40)
    +home_btn.set_size(80, 30)
    +home_btn.add_style(btn_style, lv.PART_MAIN | lv.STATE_DEFAULT)
    +home_label = lv.label(home_btn)
    +home_label.set_text(lv.SYMBOL_OK)                 # set text as Home icon
    +home_label.center()
    +
    +#- callback function when a button is pressed, react to EVENT_CLICKED event -#
    +
    +def btn_clicked_cb(obj, event)
    +    var btn = "Unknown"
    +    if   obj == prev_btn  btn = "Prev"
    +    elif obj == next_btn  btn = "Next"
    +    elif obj == home_btn  btn = "Home"
    +    end
    +    print(btn, "button pressed")
    +end
    +
    +
    +prev_btn.add_event_cb(btn_clicked_cb, lv.EVENT_CLICKED, 0)
    +next_btn.add_event_cb(btn_clicked_cb, lv.EVENT_CLICKED, 0)
    +home_btn.add_event_cb(btn_clicked_cb, lv.EVENT_CLICKED, 0)
    +

    You should see this:

    lvgl8

    IMG_1137

    Setting an input device is simple, we are now configuring the three buttons as the equivalent of a rotary encoder: left/ok/right.

    To control focus, you need to create a group, put the focusable items in the group, and assign the input device to the group:

    g = lv.group()
    +g.add_obj(prev_btn)
    +g.add_obj(home_btn)
    +g.add_obj(next_btn)
    +rotary = lv.register_button_encoder(true)   #- buttons are inverted -#
    +rotary.set_group(g)
    +

    Touch Screen Support~

    colorwheel

    Touch screen are supported natively via Universal Display driver.

    Example:

    colp = lv.colorwheel(scr, false)
    +colp.set_size(130, 130)
    +colp.set_pos(10,30)
    +

    Let's go into the details of this example.

    Starting LVGL~

    Start LVGL

    lv.start()
    +

    Note: when you create an LVGL object, you need to use the lv module. For example, creating a label object is done with lv.lv_label. As a convenience, classes can also be named with a shorter name lv.label which is equivalent to lv.lv_label. The internal class name is still lv_label.

    Use lv.montserrat_font(<size>) to load a pre-defined montserrat font. Embedded sizes are: 10, 14, 20, 28. You can also load a font from the file-system but you need to convert them first. See: https://docs.lvgl.io/latest/en/html/overview/font.html

    hres = lv.get_hor_res()       # should be 320
    +vres = lv.get_ver_res()       # should be 240
    +
    +scr = lv.scr_act()            # default screan object
    +f20 = lv.montserrat_font(20)  # load embedded Montserrat 20
    +

    Set the background color~

    #- Background with a gradient from black #000000 (bottom) to dark blue #0000A0 (top) -#
    +scr.set_style_bg_color(lv.color(0x0000A0), lv.PART_MAIN | lv.STATE_DEFAULT)
    +scr.set_style_bg_grad_color(lv.color(0x000000), lv.PART_MAIN | lv.STATE_DEFAULT)
    +scr.set_style_bg_grad_dir(lv.GRAD_DIR_VER, lv.PART_MAIN | lv.STATE_DEFAULT)
    +

    The display is composed of a virtual screen object scr. To change the background you need to change the style of this object. You can either create a full style object or change the style inside the object. This is what we do here. Hence methods: set_style_<xxx>

    In this example we do a vertical color gradient from dark blue (up) to black (down).

    Colors~

    Color are created via lv.color(<int>) with 0xRRGGBB as 24 bits color. Internally the color is converted to the display color depth so rounding errors may happen:

    > lv.color(0x808080)
    +lv_color(0x838183 - native:0x1084)
    +

    The line above shows the internal color converted back to 24 bits RGB (rounding errors occur) and the native 15 bits RGB internal color.

    Create the upper text line~

    #- Upper state line -#
    +stat_line = lv.label(scr)
    +if f20 != nil stat_line.set_style_text_font(f20, lv.PART_MAIN | lv.STATE_DEFAULT) end
    +stat_line.set_long_mode(lv.LABEL_LONG_SCROLL)                                        # auto scrolling if text does not fit
    +stat_line.set_width(hres)
    +stat_line.set_align(lv.TEXT_ALIGN_LEFT)                                              # align text left
    +stat_line.set_style_bg_color(lv.color(0xD00000), lv.PART_MAIN | lv.STATE_DEFAULT)    # background #000088
    +stat_line.set_style_bg_opa(lv.OPA_COVER, lv.PART_MAIN | lv.STATE_DEFAULT)            # 100% background opacity
    +stat_line.set_style_text_color(lv.color(0xFFFFFF), lv.PART_MAIN | lv.STATE_DEFAULT)  # text color #FFFFFF
    +stat_line.set_text("Tasmota")
    +stat_line.refr_size()                                                                # new in LVGL8
    +stat_line.refr_pos()                                                                 # new in LVGL8
    +

    Let's decompose:

    stat_line = lv.label(scr)
    +

    Creates an object of type lv_label with parent scr (screen).

    if f20 != nil stat_line.set_style_text_font(f20, lv.PART_MAIN | lv.STATE_DEFAULT) end
    +

    If f20 is correctly loaded, set the font to Montserrat 20. Styles are associated to parts of objects and to states. Here we associate to the main part for state default.

    stat_line.set_long_mode(lv.LABEL_LONG_SCROLL)                                        # auto scrolling if text does not fit
    +

    Set the label to auto roll from right to left and vice versa if the text does not fit in the display.

    stat_line.set_width(hres)
    +stat_line.set_align(lv.TEXT_ALIGN_LEFT)                                              # align text left
    +

    Set the width to full screen resolution, and align text to the left.

    stat_line.set_style_bg_color(lv.color(0xD00000), lv.PART_MAIN | lv.STATE_DEFAULT)    # background #000088
    +stat_line.set_style_bg_opa(lv.OPA_COVER, lv.PART_MAIN | lv.STATE_DEFAULT)            # 100% background opacity
    +stat_line.set_style_text_color(lv.color(0xFFFFFF), lv.PART_MAIN | lv.STATE_DEFAULT)  # text color #FFFFFF
    +

    Set background color to red, text color to white, opacity to 100%.

    stat_line.set_text("Tasmota")
    +

    Set the text of the label.

    stat_line.refr_size()                                                                # new in LVGL8
    +stat_line.refr_pos()                                                                 # new in LVGL8
    +

    The latter is new in LVGL8 and tells the widget to update its size and position, that we will use right after to position other widgets. Please note that the actual display is asynchronous. We describe the objects, in whatever order, they will be all displayed at once.

    Create a style~

    #- create a style for the buttons -#
    +btn_style = lv.style()
    +btn_style.set_radius(10)                        # radius of rounded corners
    +btn_style.set_bg_opa(lv.OPA_COVER)              # 100% background opacity
    +if f20 != nil btn_style.set_text_font(f20) end  # set font to Montserrat 20
    +btn_style.set_bg_color(lv.color(0x1fa3ec))      # background color #1FA3EC (Tasmota Blue)
    +btn_style.set_border_color(lv.color(0x0000FF))  # border color #0000FF
    +btn_style.set_text_color(lv.color(0xFFFFFF))    # text color white #FFFFFF
    +

    We create a lv_style object and associate some attributes. This works similarly to CSS styles. This style sets background color to Tasmota button blue, text to white, opacity to 100%, font to Montserrat 20 and corner rounding to 1 pixel (10 decipixels).

    Create the buttons~

    home_btn = lv.btn(scr)                            # center button
    +home_btn.set_pos(120,vres-40)
    +home_btn.set_size(80, 30)
    +home_btn.add_style(btn_style, lv.PART_MAIN | lv.STATE_DEFAULT)
    +home_label = lv.label(home_btn)
    +home_label.set_text(lv.SYMBOL_OK)                 # set text as Home icon
    +home_label.center()
    +

    Finally create a lv_btn object with parent scr, set its size and position, add the previously defined style and set its text.

    LVGL provides some pre-defined symbols like lv.SYMBOL_OK.

    Advanced features and extensions~

    Screenshot~

    Tasmota includes an easy way to take screenshots.

    Just use lv.screenshot() and a BMP file will be stored in the file system.

    Example:

    > lv.screenshot()
    +/screenshot-1642356919.bmp
    +

    Then download the file to your local computer. The file format is uncompressed BMP with 16 bits per pixel. It is highly recommended to then compress the image to PNG or JPG with the software of your choice.

    Warning: due to internal implementation limitations, the image is stored upside down. Don't forget to vertically revert the image.

    screenshot-1642356919

    PNG Image support~

    Support for PNG decoding depends on #define USE_LVGL_PNG_DECODER - which is enabled by default in Tasmota32-lvgl.

    You need to first store images on the file system, and simply load them through LVGL standard way. PNG identification depends on the .png extension.

    Example: store the following image as Sunrise320.png

    Sunrise320.png

    sunrise = lv.img(scr)                   # create an empty image object in the current screen
    +sunrise.set_src("A:/Sunrise320.png")    # load "Sunrise320.png", the default drive letter is 'A:'
    +sunrise.move_background()               # move the image to the background
    +

    screenshot-1642357636

    Freetype fonts support~

    Support for Freetype fonts depends on #define USE_LVGL_FREETYPE - which is NOT enabled by default in Tasmota32-lvgl.

    Bitmat fonts typically consume significant flash size because you need to embed the font at different size. Using FreeType vector fonts can bring more flexibility and options. You need to first upload the desired fonts on the Tasmota file system.

    To create the lv_font object, use lv.load_freetype_font(name:string, size:int, type:int) -> nil or lv_font. If the font is not found, the call returns nil. type can be 0 or lv.FT_FONT_STYLE_NORMAL, or a combination of lv.FT_FONT_STYLE_ITALIC and lv.FT_FONT_STYLE_BOLD.

    Example (after loading lvgl_demo.be) using sketchbook.ttf font:

    sb120 = lv.load_freetype_font("sketchbook.ttf", 120, 0)
    +tt = lv.label(scr)
    +tt.set_style_bg_opa(lv.OPA_0, lv.PART_MAIN | lv.STATE_DEFAULT)
    +tt.set_style_text_color(lv.color(0xFFFFFF), lv.PART_MAIN | lv.STATE_DEFAULT)
    +tt.set_text("MQTT")
    +tt.set_pos(10,40)
    +tt.set_size(300,150)
    +if sb120 != nil tt.set_style_text_font(sb120, lv.PART_MAIN | lv.STATE_DEFAULT) end
    +

    lv_freetype_sketch

    What's implemented and what's not?~

    What's implemented currently:

    • All standard LVGL widgets are available, most of extras
    • Styles
    • File-system
    • Fonts, currently Montserrat fonts are embedded at sizes 10, 14 (default), 20 and 28 (compressed - smaller and slower)
    • External Fonts in file-system, either in LVGL's binary format of TrueType fonts via the FreeType library (requires #defined USE_LVGL_FREETYPE)
    • Images in file-system, either in LVGL's binary format or PNG
    • Most of the high-level LVGL APIs via the lv Berry object
    • SPI displays with 16 bits per pixels
    • Animations via Berry code
    • Touch Screen support
    • SPI DMA
    • Callbacks on LVGL objects to react on events
    • Ability to define custom widgets in Berry

    What will probably not be implemented

    • Native LVGL animation engine
    • Styles garbage collection is not done, which means that creating lots of styles leads to memoly leak
    • multi-screens display - I don't know of a single ESP32 based device with multi-screens
    • Bidirectional fonts - unless there is strong demand
    • LVGL tasks - Berry provides all the necessary tools for task scheduling

    Converting C LVGL to Berry~

    Simply speaking, you can convert most constants from their C equivalent to berry by just changing the LV_ prefix to lv..

    Example: LV_SYMBOL_OK becomes lv.SYMBOL_OK

    Berry provides an object model to lv_object and sub-classes for widhets like lv_btn, lv_label... To create an object, just instantiate the class: lv_btn(parent)

    lv_style is created independently.

    lv_color takes a 24 bits 0xRRGGB as parameter, or a pre-defined color like lv.BLUE

    Compiling for LVLG~

    In my_user_config.h or in your config override, add:

    #define USE_LVGL
    +#define USE_DISPLAY
    +#define USE_DISPLAY_LVGL_ONLY
    +#define USE_XPT2046
    +#define USE_UNIVERSAL_DISPLAY
    +  #undef USE_DISPLAY_MODES1TO5
    +  #undef USE_DISPLAY_LCD
    +  #undef USE_DISPLAY_SSD1306
    +  #undef USE_DISPLAY_MATRIX
    +  #undef USE_DISPLAY_SEVENSEG
    +

    Be aware that it adds 440Kb to you firmware, so make sure you have a partition with enough program Flash space. Preferably use esp32_partition_app1856k_spiffs320k.csv partition file.

    Goodies~

    Get a Tasmota logo:

    # start the display
    +lv.start()
    +
    +# set background color to blue
    +scr = lv.scr_act()
    +scr.set_style_bg_color(lv.color(lv.COLOR_BLUE), lv.PART_MAIN | lv.STATE_DEFAULT)
    +
    +# create a lv_img object and set it to Tasmota logo
    +logo = lv.img(scr)
    +logo.set_tasmota_logo()
    +logo.center()
    +

    screenshot-1618843384

    The logo is black, with anti-aliasing and transparency. You can now manipulate the logo: change zoom, rotate or recolor.

    # recolor logo to white
    +logo.set_style_img_recolor_opa(255, lv.PART_MAIN | lv.STATE_DEFAULT)
    +logo.set_style_img_recolor(lv.color(lv.COLOR_WHITE), lv.PART_MAIN | lv.STATE_DEFAULT)
    +
    +# zoom by 125% - 100% is 256, so 125% is 320
    +logo.set_zoom(300)
    +
    +# rotate by 30 degrees - or 300 deci-degrees
    +logo.set_angle(300)
    +

    screenshot-1618843394

    Example of animation:

    cur_zoom = 200
    +cur_incr = 5
    +def animate_logo()
    +  cur_zoom += cur_incr
    +  if cur_zoom > 300 cur_incr = - cur_incr end
    +  if cur_zoom < 200 cur_incr = - cur_incr end
    +  logo.set_zoom(cur_zoom)
    +  tasmota.set_timer(100, animate_logo)
    +end
    +animate_logo()
    +

    Calibrate a resitive Touch Screen~

    Some touchscreens like Lolin TFT 2.4 Touch Shields use a resistive touchscreen controlled by XPT2046. Contrary to capacitive touchscreens, resistive touchscreens needs a per-device calibration.

    You can download DisplayCalibrate.tapp Tasmota Application which allows for easy calibration. In only a few steps, it will generate the universal display line :M with calibration information.

    1. First download DisplayCalibrate.tapp application and upload it in the file system, and restart.

    2. Make sure you are in orientation DisplayRotate 0

    3. In the console, type the command DisplayCalibrate

    You will see the following screens. Click on all 4 crosses near corners.

    ts_0

    ts_1_0

    ts_1_1

    ts_1_2

    ts_1_3

    Note: measures are taken every 50 ms and are averaged, and requires at least 3 measures (150ms).

    If everything went well, you will see the following screen. After reboot, your touchscreen is ready and calibrate.

    ts_ok

    If the geometry is wrong, you will see the following screen and no change is done to display.ini

    ts_nok

    Cookbook~

    Measuring user inactivity~

    LVGL has a notion of screen inactivity, i.e. how long did the user not interact with the screen. This can be use to dim the display or turn it off after a moment of inactivity (like a screen saver). The time is in milliseconds. Full doc here: https://docs.lvgl.io/8/overview/display.html#inactivity

    # time of inactivity in ms
    +lv.disp().get_inactive_time()
    +

    Technical Details~

    The code size impact is quite significant, so you probably need partitions with code at least set to 1856KB. Doing so leaves 320KB for file system on 4MB flash. With the Safeboot partition layout, default code partition size for 4MB of flash is 2880KB.

    Most of Berry code is solidified in Flash, so the initial RAM footprint is very low (a few KB).

    \ No newline at end of file diff --git a/LVGL_Internals/index.html b/LVGL_Internals/index.html new file mode 100644 index 0000000000..5db979735f --- /dev/null +++ b/LVGL_Internals/index.html @@ -0,0 +1,93 @@ + LVGL - Internals - Tasmota
    Skip to content

    LVGL - Internals~

    Below are notes about the LVGL-Berry mapping in Tasmota. You will find information for curious people and maintainers.

    Build system~

    Berry mapping to LVGL is entirely automated.

    Most of the components are generated C code from the LVGL's C source code, similar to MicroPython approach.

    Phase 1: Parse LVGL source

    This first phase parses most C headers from the LVGL source tree and generates two files: - lv_enum.h containing all the enum values from LVGL (constants) - lv_funcs.h containing all the functions of the LVGL API normalized to 1 function per line, and with cleaned argument signature.

    (in folder Tasmota/lib/libesp32_lvgl/lv_berry/tools)
    +
    +❯ python3 preprocessor.py
    +
    +(no output)
    +

    Phase 2: Generate automatic Berry mapping

    From the two files created in the previous step, all the requires C files are created for the Berry mapping.

    (in folder Tasmota/lib/libesp32_lvgl/lv_berry/tools)
    +
    +> python3 convert.py
    +| callback types['lv_group_focus_cb', 'lv_event_cb', 'lv_constructor_cb', 'lv_layout_update_cb', 'lv_obj_tree_walk_cb', 'lv_theme_apply_cb', 'lv_color_filter_cb']
    +

    The output should look as above, and indicates the C function that have been ignored (if any) if their return type is listed above. It also lists the callback types supported.

    Phase 3: Generate the Berry pre-compiled stubs

    This phase is specific to Berry pre-compiled modules and classes.

    (in folder Tasmota/lib/libesp32/berry)
    +
    +> ./gen.sh
    +
    +(no output)
    +

    Phase 4: compile Tasmota using platform.io as usual

    lv module~

    Tasmota automatically and implicitly imports lv module if compiled with LVGL.

    import lv
    +

    The lv module is solidified in Flash, so to make it extensible, there is a trick applied to it.

    When you do import lv the first time, a hidden lv_new module is created in memory (writable) and a member function is added so that all members requested that are not part of lv_new are diverted to lv.

    Concretely, this means that the new lv module is a facade to the read-only solidified lv module, but you can still add methods.

    This is how it is done internally:

    lv = module("lv")
    +
    +# rename `lv` to `lv_ntv` and replace `lv` with `lv_tasmota`
    +def lv_module_init(lv_solidified)
    +  var lv_new = module("lv")   # create a dynamic module
    +  lv_new.member = lv_solidified.member
    +  # lv_new.lv_solidified = lv_solidified
    +  return lv_new
    +end
    +
    +lv.init = lv_module_init
    +
    +def lv0_member_ntv() end
    +
    +lv.member = lv0_member_ntv
    +
    +return lv
    +

    Tasmota then does import lv_tasmota to add all Tasmota specific extensions to module lv.

    Constants~

    The lv module is a placeholder for all LVGL constants, the equivalent of C enums.

    As a rule of thumb, all C constants are mapped with a similar name. Just replace LV_<name> with lv.<name>.

    Example: C API LV_LABEL_ALIGN_LEFT becomes in Berry lv.LABEL_ALIGN_LEFT

    Implementation~

    The C enum constants are all compiled in a single file tools/lv_berry/lv_module.h. Only names are listed, the actual values are retrieved by the C compiler at runtime (which avoids many mistakes).

    Internally constants are handled by a virtual member in lvgl module. The module lvgl has a member() function that is called when the Berry runtime does not know the member name.

    The search happens in lv0_member() which first searches for a static member name, and if not found, looks for a widget class name.

    Constants are put in a C table in lib/libesp32/Berry/default/be_lv_lvgl_module.c as lv0_constants[]. The table is sorted by member name to allow for fast binary search (dichotomy).

    const be_constint_t lv0_constants[] = {
    +
    +    { "ALIGN_CENTER", LV_ALIGN_CENTER },
    +    { "ALIGN_IN_BOTTOM_LEFT", LV_ALIGN_IN_BOTTOM_LEFT },
    +   [...]
    +    { "WIN_PART_SCROLLBAR", LV_WIN_PART_SCROLLBAR },
    +    { "YELLOW", 16776960 },
    +};
    +

    Colors~

    An exception for LVGL colors, they are defined as 32 bits RGB values as follows, and not based on their C representation:

    COLOR_WHITE=0xFFFFFF
    +COLOR_SILVER=0xC0C0C0
    +COLOR_GRAY=0x808080
    +COLOR_BLACK=0x000000
    +COLOR_RED=0xFF0000
    +COLOR_MAROON=0x800000
    +COLOR_YELLOW=0xFFFF00
    +COLOR_OLIVE=0x808000
    +COLOR_LIME=0x00FF00
    +COLOR_GREEN=0x008000
    +COLOR_CYAN=0x00FFFF
    +COLOR_AQUA=0x00FFFF
    +COLOR_TEAL=0x008080
    +COLOR_BLUE=0x0000FF
    +COLOR_NAVY=0x000080
    +COLOR_MAGENTA=0xFF00FF
    +COLOR_PURPLE=0x800080
    +

    Example: lv.COLOR_RED

    Widgets classes~

    Although LVGL is C code and is not formally object oriented, LVGL widget follow an inheritance model. Each widget is a virtual subclass of lv_obj structure.

    Berry builds an actual Object Oriented class system, with a base class lv_obj and subclasses.

    The class names supported are defined in convert.py and are currently:

    'lv_arc', 'lv_bar', 'lv_btn', 'lv_btnmatrix', 'lv_calendar', 'lv_canvas', 'lv_chart', 'lv_checkbox',
    +'lv_cont', 'lv_cpicker', 'lv_dropdown', 'lv_gauge', 'lv_img', 'lv_imgbtn', 'lv_keyboard', 'lv_label', 'lv_led', 'lv_line',
    +'lv_linemeter', 'lv_list', 'lv_msgbox', 'lv_objmask', 'lv_templ', 'lv_page', 'lv_roller', 'lv_slider', 'lv_spinbox',
    +'lv_spinner', 'lv_switch', 'lv_table', 'lv_tabview', 'lv_textarea', 'lv_tileview', 'lv_win'
    +

    Additional 'special' classes are (they do not inherit from lv_obj):

    'lv_obj', 'lv_group', 'lv_style', 'lv_indev'
    +

    Parsing~

    The parsing is done by convert.py which parses tools/lv_berry/lv_widgets.h. This file contains all the C function signatures as single lines. convert.py checks if the types are supported and converts it as a Berry signature.

    The resulting signatures are used to generate class stubs for all Berry classes in lib/libesp32/Berry/default/be_lvgl_widgets_lib.c and the Berry signatures are in tasmota/lvgl_berry/be_lv_c_mapping.h

    Example:

    The C signature:

    bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area);
    +

    is recognized to be part of lv_obj class (by prefix) and has the following signature:

    { "area_is_visible", (void*) &lv_obj_area_is_visible, "b", "(lv_obj)(lv_area)" },
    +

    Decomposed as: - "area_is_visible": name of the Berry method - (void*) &lv_obj_area_is_visible: pointer to the C implementation - "b": return type, here boolean - "(lv_obj)(lv_area)": input types, 2 arguments of classes lv_obj and lv_area

    Other example:

    void lv_btnmatrix_set_align(lv_obj_t * btnm, lv_label_align_t align);  
    +
    +{ "set_align", (void*) &lv_btnmatrix_set_align, "", "(lv_obj)i" },
    +

    The parsing of the signature is done in be_check_arg_type()

    Input and output types are:

    • "b": boolean
    • "s": string
    • "i": int (signed 32 bits)- ".": any type
    • "&": where n is a digit, Berry callback by class (see below)
    • "(lv_)": an instance of lv_. Note if you pass 0 (NULL) to a class argmunent it is accepted without warning.

    Note: any missing argument or nil argument is converted to 0.

    In case of an argument mismatch, a warning is printed but the call is still proceed.

    Warning: you can easily crash Tasmota if you send wrong types arguments.

    Widgets instantiation~

    Instantiation of a widget is marked as a specific signature. The return type is prefixed with +:

    lv_obj_t * lv_canvas_create(lv_obj_t * par, const lv_obj_t * copy);
    +
    +{ "create", (void*) &lv_canvas_create, "+lv_canvas", "(lv_obj)(lv_obj)" },
    +

    All widgets constructor always take 2 arguments, the first is the parent object, the second is the copy object (generally null or ignored)

    Example:

    scr = lv.scr_act()
    +log = lv_label(scr)   # scr is parent object of log
    +

    Internally, widget constructors call lvx_init_2(). LVGL object are allocated by LVGL, the Berry object only contains a reference to the C structure (a pointer). These objects can be garbage collected without any impact.

    lv_obj and widget constructors also accept a specific form: log2 = lv_label(-1, log) which just creates a second reference to the same LVGL object - it is mostly used internally to dynamically create an instance from a returned C pointed.

    Callbacks~

    Callbacks are a challenge in Berry. A callback is only a C pointer to a function and does not natively hold any other information. However we would like to match a single C address to multiple Berry closures.

    We take into account the fact that the first argument of any LVGL callback has always an instance as first argument, from the type list: 'lv_group_focus_cb', 'lv_event_cb', 'lv_signal_cb', 'lv_design_cb', 'lv_gauge_format_cb'

    We define 5 different C functions with 5 distinct addresses, one for each callback type. Then we use the first argument to dispatch the call to the appropriate Berry closure.

    Here is the call used at startup:

    import lvgl as lv
    +
    +# for each callback type, mapping between first argument and closure
    +_lvgl_cb = [ {}, {}, {}, {}, {}, {} ]
    +
    +# for each callback type, mapping between first argument and the actual Berry object with the correct type (C arguments are not explicitly typed)
    +_lvgl_cb_obj = [ {}, {}, {}, {}, {}, {} ]
    +
    +def _lvgl_cb_dispatch(idx, obj, v1, v2, v3, v4)
    +  var func = _lvgl_cb[idx].find(obj)
    +  var inst = _lvgl_cb_obj[idx].find(obj)
    +  if func != nil
    +    return func(inst, v1, v2, v3, v4)
    +  end
    +  return nil
    +end
    +

    Styles~

    lv_style is not a subclass of lv_obj but uses a similar mechanism to map the members.

    Main difference, it uses a distinct constructor lvs_init().

    Note: lv_style needs to allocate memory and must not be garbage collected. For this reason lv_style allocates static memory which is never freed. Be aware that it may be a cause of memory leak (although not very likely).

    Colors~

    lv_color is a simple class that maps RGB 32 bits colors (as 32 bits int) to the internal representation of colors (usually 16 bits).

    Don't be surprised that getting back a value is the 16 bits color converted to 32 bits - rounding errors may occur:

    [Berry Console]
    +> c = lv_color(0x808080)
    +> c
    +lv_color(0xff838183 - native:0x1084)
    +

    Note:

    • 0xff838183 - is the 32 bits color, with alpha channel (opaque)
    • 0x1084 - is the native internal representation as 16 bits color with swapped bytes

    Groups~

    lv_group behaves like lv_obj but does not inherit from it.

    Indev~

    Indev or 'Input Device' is a simple class wrapper to handle touch screens and input buttons. It is similar to lv_obj but uses a simple constructor lv0_init() that just wraps the C pointer into the Berry instance.

    \ No newline at end of file diff --git a/LVGL_in_10_minutes/index.html b/LVGL_in_10_minutes/index.html new file mode 100644 index 0000000000..a458649712 --- /dev/null +++ b/LVGL_in_10_minutes/index.html @@ -0,0 +1,82 @@ + LVGL in less than 10 minutes with Tasmota - Tasmota
    Skip to content

    LVGL in less than 10 minutes with Tasmota~

    In 2021, Tasmota added full support of LVGL for ESP32 based devices. It also introduced the Berry scripting language, a small-footprint language similar to Python and fully integrated in Tasmota. A comprehensive mapping of LVGL in Berry language is available, similar to the mapping of Micropython.

    The tutorial below illustrates how to install Tasmota on M5Stack devices (based on ESP32) and run your first LVGL application in Berry - all in less than 10 minutes.

    We will use nice M5Stack devices that integrate ESP32 and a 320x240 display, with touchscreen or physical buttons depending on the device.

    Flash Tasmota-lvgl on the device~

    We have packaged an easy to use Web Flasher for ESP32.

    If this method fails or if you prefer local client method, have a look at Getting Started on Tasmota documentation.

    • Scroll down in Development section and select Tasmota32 LVGL

    • Click Connect and select the appropriate Serial Port. It should connect in a couple of second.

    • Click Install Development Tasmota32 Lvgl (English)

    • Check Erase Device if it's the first time you install Tasmota, or you may experience unwanted behavior. Click Next

    • Click Install

    • It should take a little more to Erase and Flash

    You can now close the Web Flasher and proceed to the Wifi configuration

    Configure Tasmota Wifi~

    The next step can be done with a phone or a computer.

    • Connect to temporary the wifi access point with a name starting with tasmota-...

    • You should be automatically brought to the Wifi configuration page. If not, try connecting to 192.168.4.1

    • Select your wifi network (yes, my wifi is named Tasmota...), enter the password and hit Save. If all goes well, you should see the IP address of Tasmota and be redirected to it.

    • You should now see the Tasmota main screen.

    Configure Tasmota for M5Stack~

    Tasmota is based on general purpose firmwares, you now need to configure specifically for your M5Stack device. We provide two templates, one for M5Stack Basic/Gray/GO/Fire and one for M5Stack Core2.

    • Go to Tasmota main screen and click on Configuration

    • Then click on Auto-configuration. You are now on the Auto-configuration page. Select M5Stack Fire or M5Stack Core2. Then click on Apply configuration.

    The device restarts and you should be back to Tasmota main page. The screen should have been configured (you should see a short flash on the screen).

    Try a LVGL demo app~

    In this last step, we will try to run a small LVGL application written in Berry.

    • Go to Tasmota Main Screen and click Consoles then Manage File System

    • Click on Create and edit new file, enter the filename autoexec.be and copy the following code in the text pane:

    #- start LVGL and init environment -#
    +lv.start()
    +
    +tasmota.cmd("DisplayDimmer 50")
    +
    +hres = lv.get_hor_res()       # should be 320
    +vres = lv.get_ver_res()       # should be 240
    +
    +scr = lv.scr_act()            # default screean object
    +f20 = lv.montserrat_font(20)  # load embedded Montserrat 20
    +
    +#- Background with a gradient from black #000000 (bottom) to dark blue #0000A0 (top) -#
    +scr.set_style_bg_color(lv.color(0x0000A0), lv.PART_MAIN | lv.STATE_DEFAULT)
    +scr.set_style_bg_grad_color(lv.color(0x000000), lv.PART_MAIN | lv.STATE_DEFAULT)
    +scr.set_style_bg_grad_dir(lv.GRAD_DIR_VER, lv.PART_MAIN | lv.STATE_DEFAULT)
    +
    +#- Upper state line -#
    +stat_line = lv.label(scr)
    +if f20 != nil stat_line.set_style_text_font(f20, lv.PART_MAIN | lv.STATE_DEFAULT) end
    +stat_line.set_long_mode(lv.LABEL_LONG_SCROLL)                                        # auto scrolling if text does not fit
    +stat_line.set_width(hres)
    +stat_line.set_align(lv.TEXT_ALIGN_LEFT)                                              # align text left
    +stat_line.set_style_bg_color(lv.color(0xD00000), lv.PART_MAIN | lv.STATE_DEFAULT)    # background #000088
    +stat_line.set_style_bg_opa(lv.OPA_COVER, lv.PART_MAIN | lv.STATE_DEFAULT)            # 100% background opacity
    +stat_line.set_style_text_color(lv.color(0xFFFFFF), lv.PART_MAIN | lv.STATE_DEFAULT)  # text color #FFFFFF
    +stat_line.set_text("Tasmota")
    +stat_line.refr_size()                                                                # new in LVGL8
    +stat_line.refr_pos()                                                                 # new in LVGL8
    +
    +#- display wifi strength indicator icon (for professionals ;) -#
    +wifi_icon = lv_wifi_arcs_icon(stat_line)    # the widget takes care of positioning and driver stuff
    +clock_icon = lv_clock_icon(stat_line)
    +
    +#- create a style for the buttons -#
    +btn_style = lv.style()
    +btn_style.set_radius(10)                        # radius of rounded corners
    +btn_style.set_bg_opa(lv.OPA_COVER)              # 100% background opacity
    +if f20 != nil btn_style.set_text_font(f20) end  # set font to Montserrat 20
    +btn_style.set_bg_color(lv.color(0x1fa3ec))      # background color #1FA3EC (Tasmota Blue)
    +btn_style.set_border_color(lv.color(0x0000FF))  # border color #0000FF
    +btn_style.set_text_color(lv.color(0xFFFFFF))    # text color white #FFFFFF
    +
    +#- create buttons -#
    +prev_btn = lv.btn(scr)                            # create button with main screen as parent
    +prev_btn.set_pos(20,vres-40)                      # position of button
    +prev_btn.set_size(80, 30)                         # size of button
    +prev_btn.add_style(btn_style, lv.PART_MAIN | lv.STATE_DEFAULT)   # style of button
    +prev_label = lv.label(prev_btn)                   # create a label as sub-object
    +prev_label.set_text("<")                          # set label text
    +prev_label.center()
    +
    +next_btn = lv.btn(scr)                            # right button
    +next_btn.set_pos(220,vres-40)
    +next_btn.set_size(80, 30)
    +next_btn.add_style(btn_style, lv.PART_MAIN | lv.STATE_DEFAULT)
    +next_label = lv.label(next_btn)
    +next_label.set_text(">")
    +next_label.center()
    +
    +home_btn = lv.btn(scr)                            # center button
    +home_btn.set_pos(120,vres-40)
    +home_btn.set_size(80, 30)
    +home_btn.add_style(btn_style, lv.PART_MAIN | lv.STATE_DEFAULT)
    +home_label = lv.label(home_btn)
    +home_label.set_text(lv.SYMBOL_OK)                 # set text as Home icon
    +home_label.center()
    +
    +#- callback function when a button is pressed, react to EVENT_CLICKED event -#
    +
    +def btn_clicked_cb(obj, event)
    +    var btn = "Unknown"
    +    if   obj == prev_btn  btn = "Prev"
    +    elif obj == next_btn  btn = "Next"
    +    elif obj == home_btn  btn = "Home"
    +    end
    +    print(btn, "button pressed")
    +end
    +
    +prev_btn.add_event_cb(btn_clicked_cb, lv.EVENT_CLICKED, 0)
    +next_btn.add_event_cb(btn_clicked_cb, lv.EVENT_CLICKED, 0)
    +home_btn.add_event_cb(btn_clicked_cb, lv.EVENT_CLICKED, 0)
    +
    • Click Save and restart the device. You should now see this on the M5Stack screen:

    You will find more information on LVGL-Tasmota-Berry mapping.

    \ No newline at end of file diff --git a/LedMask/index.html b/LedMask/index.html new file mode 100644 index 0000000000..4c99d42c46 --- /dev/null +++ b/LedMask/index.html @@ -0,0 +1 @@ + LedMask - Tasmota

    LedMask

    LedMask command allows setting a bitmask which specifies which relays control the LED indicator used to display whether a relay is latched/powered. The order of the <bitmask> is from most significant bit (MSB) to least significant bit (LSB). Bit 15 (MSB) masks Relay16 through bit 0 (LSB) which masks Relay1, respectively. For each relay to be included in controlling the power LED, set its corresponding bit in the <bitmask> to 1. <bitmask> bits without corresponding configured relay components have no effect and can be ignored.

    <bitmask> = bitwise value representing each relay. Values may be entered as either hexadecimal or decimal values (e.g., 0xFFFF = 65535).
    0xFFFF (= 1111 1111 1111 1111) All relays control the power LED (default)

    LedState must be enabled (i.e., != 0) in order for LedMask to take effect.

    Examples:

  • `LedMask 0xFFFD` Every relay, except Relay2, controls the power LED (0xFFFD = 1111 1111 1111 1101)
  • `LedMask 0x0002` Only Relay 2 controls the power LED (0x0002 = 0000 0000 0000 0010)
  • \ No newline at end of file diff --git a/Lights/index.html b/Lights/index.html new file mode 100644 index 0000000000..fed0fae958 --- /dev/null +++ b/Lights/index.html @@ -0,0 +1,2 @@ + Lights - Tasmota
    Skip to content

    Lights

    You know what lights do.... Right? 💡

    Control Lights~

    with WebUI~

    Tasmota_on-off

    Tasmota webUI displays Brightness, CT, White, Color Picker, Color Saturation or PWM Level sliders depending on the light component, the number of PWM channels configured and SetOptions used.


    HSV

    Tasmota uses a HSB color model, which besides other more subtile differences compared to HSL means, that the color must be desaturated to reach complete black or white.

    Control Range Commands and details
    Brightness 0..100 (percent) Dimmer, HSBColor3: Brightness of the light
    Hue 0..359 (degrees) HSBColor1: Color as an angle in the color wheel
    Hue
    Sat 0..100 (percent) HSBColor2: saturation of the color, 0=grey/white, 100=pure color
    CT 153..500 (mireds) CT: white color temperature, from 153 (Cold White) to 500(Warm White)
    Mireds

    with Commands~

    See light commands for how to control lights.

    Light Types~

    Switched Lights aka Relays~

    Tasmota_on-off

    Switched or On/Off lights are controlled through Relay GPIOs.

    If you define multiple relays, they are controlled with Power<x> starting at x=1.

    Alexa: You can use Wemo emulation, your device will appear as a switch. You can change it to a light in the Alexa app.

    Alexa: If you have one or multiple relays, you can use Philips Hue emulation. All devices will appear as On/Off lights, and named accordingly to FriendlyName. Note: If you have only Echo Spot 2nd generation, your light will have a dummy dimmer.


    Configuration (see below)
    Commands Power
    Configuration none

    1 Channel - Dimmable Light~

    Example light 1 channel

    1 channel lights are often white lights with On/Off controls and dimmer.

    Alexa: You can use Philips Hue emulation, the light will appear as white light with dimmer.
    Robotdyn-Dimmermodul

    Leading edge dimmer: You can also configure a leading edge dimmer on 230V with the 1 channel configuration. In this case you need a TRIAC and a zero-cross detection that give a pulse with every crossing of the 0V of the sinus curve. The dimmer is power callibrated. 10% --> 10% light or power consumption on a device like a heater. The Dimmer is e.g. suitable to dynamically pump remaining solar power into a heat sink.

    Robotdyn AC dimmer configuration

    Connect zero-crossing to GPIO of Counter4. Define a PWM and connect

    Configuration (see below)
    Dimmer,Channel1 PWM1
    Channel2 PWM2 (optional)
    Channel3 PWM3 (optional)
    Channelxx PWMxx (optional)
    Zero-Cross PIN COUNTER4 (mandatory)
    Commands ZCDimmerSet ESP32 only

    Example schematic:
    ZCDimmer example schematic

    Example config:
    ZCDimmer example config

    Preferably before connecting the ZC & PWM perform the following commands:

    • SetOption99 1 -> to enable detection of the raising edge of the zero-crossing
    • SetOption68 1 -> with more than ONE PWM this will all operate as single lights. Control with [channelx 1..100]
    • LedTable 0 -> for normal lamps or motors. Recommended
    • savedata 0 -> Saving the current status create small flickering. OFF prevent this. On ESP32 recommended. Do not change after reboot!
    Configuration (see below)
    Commands Power, Dimmer, Channel, Fade, Speed
    Options Auto Power On, PWM Channel Configuration, Gamma Correction

    2 Channels - CCT Light~

    CCT

    2 channels lights are white lights with correlated color temperature (CCT) controls from Cold White (CT=153) to Warm White (CT=500).

    Alexa: You can use Philips Hue emulation, the light will appear as white light with color temperature. Control through the Alexa app is limited to the CT range 199..383.

    Configuration (see below)
    Commands Power, Dimmer, Color, White, CT
    Options Auto Power On, PWM Channel Configuration, Gamma Correction, PWM CT


    3 Channels - RGB Lights~

    RGB

    3 channel lights are RGB color lights. You can set color either via RGB or HSB (not HSL). Alexa support also allows XY color, but that is not supported through commands.

    Alexa: You can use Philips Hue emulation, the light will appear as Color light.

    Configuration (see below)
    Commands Power, Dimmer, Color, HSBColor
    Options Auto Power On, PWM Channel Configuration, Gamma Correction, Channel Remapping


    4 Channels - RGBW Lights~

    RGBW-2RGBW using RGB and White Split

    4 channel lights are RGBW, i.e. RGB light and an additional White light. White can be either Warm White or Cold White depending on the manufacturer.

    Alexa: You can use Philips Hue emulation, the light will appear as Color light and White light with CT control. The CT control is only present to force pure white instead of RGB white. Changing CT will have no effect.


    There is no White only slider in the UI for 4 channel lights

    Use White commands or set up White Blend Mode or RGB and White Split.

    Configuration (see below)
    Commands Power, Dimmer, Color, HSBColor, White
    Options Auto Power On, PWM Channel Configuration, Gamma Correction, Channel Remapping, White Blend Mode, RGB and White Split

    Danger

    Some lights have limited power supply that do not allow all channels to be at full power at the same time. Be careful not to burn out your light if you force all channels to be on using Color or RGB and White Split.

    5 Channels - RGBCCT Lights~

    Tasmota_5_2RGBCCT using RGB and White Split

    5 channel lights are RGBCCT - a 3 channel RGB light and an additional 2 channel CCT light.

    Alexa: You can use Philips Hue emulation, the light will appear as Color light and White light with CT control.


    Configuration (see below)
    Commands Power, Dimmer, Color, HSBColor, White, CT
    Options Auto Power On, PWM Channel Configuration, Gamma Correction, Channel Remapping, White Blend Mode, RGB and White Split

    Danger

    Some lights have limited power supply that do not allow all channels to be at full power at the same time. Be careful not to burn out your light if you force all channels via Color or RGB and White Split

    Independent PWM Channels~

    Tasmota_multi

    Any combination of Relays and PWMs, when enabling SetOption68 1. Splits off the light into individually controlled Channels (Useful when connecting multiple 1 channel strips to a single controller)

    Configuration (see below)
    Commands Power, Channel, Color
    Options Auto Power On, PWM Channel Configuration, Gamma Correction


    Light Options~

    Gamma Correction~

    Gamma Correction is enabled by default in Tasmota (LedTable 1).

    Human eye perception of brightness is non linear, bringing back linearity needs a trick called Gamma Correction.

    Some lights have hardware gamma correction (f.e. Sonoff B1). In that case, software gamma correction should be disabled with LedTable 0.

    The curve used: orange=ideal, blue=tasmota.

    How do I know if I have hardware gamma correction?

    If you find your light very dark even with Dimmer 40, it can mean either you have hardware PWM, disable it with LedTable 0, or you need to apply a minimum PWM value, use DimmerRange 40,100 (adapt to the best value).

    Internally Tasmota uses 10 bits resolution PWM to get smoother levels at low brightness.

    When using Fade mode, a different Gamma curve (called gamma_fast). This cruve cheats slightly and makes lights increase brighness faster then normal; otherwise the light starts glowing very slowly and gives the impression that the light is not responsive.

    White Blend Mode~

    White Blend Mode mixes in the white channel with RGB colors while controlling the RGB light which results in a better and brighter color output. It is used only with 4 channel (RGBW) and 5 channel (RGBCCT) lights.

    Enable it with SetOption105 1.

    For Tasmota versions before 8.5 use command RGBWWTable 255,255,255,0 instead.

    Calibration (optional)~

    Generally white LEDs are brighter than RGB LEDs. If you want to keep the same brightness, you need to calibrate the white level. In this mode, any white component will be removed from RGB LEDs and sent to the white LEDs. This makes whites look much better.

    Example

    Color 30508000 will be converted to Color 0020503000 (0x30 is subtracted from RGB channels and added to the White channel)

    To calibrate a RGBW light:

    1. Color FFFFFF00
    2. RGBWWTable 255,255,255,255,255 - reset to RGB mode
    3. RGBWWTable 255,255,255,<n>,0 - (begin the calibration process with <n> == 150)
    4. If too bright, decrease <n>. If too dim, increase <n>
    5. Go back to step 2 and iterate until satisfied with the color intensities.

    Calibration examples for specific devices:

    Sonoff B1: RGBWWTable 255,255,255,35,0 Teckin SB50: RGBWWTable 255,255,255,80,0

    RGB and White Split~

    SetOption37 128

    By default RGBW and RGBCCT lights can only be controlled in single mode, either RGB or White (f.e. Turning on CT lights turns off RGB lights and vice versa).

    Use SetOption37 128 to split RGB and White into 2 independent lights. If you are already using Channel Remapping, just add 128 to the value of SetOption37.

    Channel Remapping~

    SetOption37

    Read More

    Disable Auto Power On~

    Lights are always powered on when a light command or a webUI slider is used and automatically powered off when color is set to black or Dimmer is set to 0.

    When enabling SetOption20 1 any change to webUI sliders or using commands CT, Dimmer, HSBColor3, Color or Channel will not automatically power on the light if it is off.

    PWM CT~

    Module 48 or SetOption92 1

    Some CCT lights use PWM1 for brightness and PWM2 for color temperature (instead of PWM1 for Cold White and PWM2 for Warm White).

    For these lights, use Module 48 aka Philips Xiaomi mode, or SetOption92 1 (supported since v.8.2.0.5)

    Virtual CT~

    this feature is experimental and will probably not give brilliant results

    Used with 4 channel RGBW lights to simulate the missing white channel (cold or warm) using RGB channels.

    Enable Virtual CT with SetOption106 1 then choose which type of white you're simulating with SetOption107 where 0 is warm white and 1 is cold white

    Light Categories~

    Lights come in various shapes (bulb, strips, ceiling lights, ...) but in Tasmota they are separated in 3 categories:

    Channel Controlled Lights~

    PWM Lights~

    Lights controlled using up to 5 channels (red, green, blue, cold white, warm white). Channels are controlled using PWM or APDM.

    PWM (Pulse Width Modulation) is the most common method of controlling LED lights.

    These lights are configured by assigning PWM1(i) through PWM5(i) components to their GPIOs; PWM<x>i means PWM is inverted. Depending on the number of used PWMs Tasmota will recognize the light as

    Channels PWM1 PWM2 PWM3 PWM4 PWM5
    1 Brightness
    2 Cold White Warm White
    3 Red Green Blue
    4 Red Green Blue White
    5 Red Green Blue Cold White Warm White

    MY92xx~

    MY92xx family of drivers uses Adaptive Pulse Density Modulation.

    Configured in Tasmota by assigning MY92x1 DI and MY92x DCKI components to their GPIOs (some devices might have more than one MY92xx controller)

    Channel mapping for such devices is dependent on the controllers but is easily remapped using SetOption37.

    SM16716~

    SM16716 LEDs, sometimes mislabelled as WS2801.

    Configured in Tasmota by assigning SM16716 CLK, SM16716 DAT and SM16716 PWR component to their GPIOs.

    Some SM16716 bulbs have BGR order and need SetOption37 54 to work properly.

    PWM Dimmer Switches~

    Specific module (requires a custom binary) for Martin Jerry/acenx/Tessan/NTONPOWER SD0x PWM dimmer switches. Brightness of the load for these dimmers is controlled by a PWM GPIO pin. They typically have power, up and down buttons, a power status LED, five brightness LEDs and another status LED. Read more...

    Addressable LEDs~

    Lights where each LED is individually controlled. In these lights it is possible to adjust each LEDs power, color and brightness, all just with the use of a single GPIO pin.

    WebUI shows hue, saturation and brightness sliders and power toggle for these lights. Red and green color may be mixed up (observed for clone of Wemos RGB shield).

    WS2812~

    These are also commonly called Neopixel lights.

    Configured in Tasmota by assigning WS2812 (7) component to its GPIO.

    For wiring, see instructions for LED strip or Wemos RGB shield.

    SK6812~

    SK6812 RGBW LEDs are not supported in standard binaries and need custom compiled firmware. See instructions.

    Status LEDs~

    Status LEDs are the LEDs on the device used to display device information

    Those LEDs are defined in a template or module using Led1, Led2, Led3 or Led4 (or Led1i, Led2i, Led3i or Led4i) and additionally using LedLink or LedLinki (LedLink was introduced in version 6.5.0.12). It is not recommended to assign Led<x> and Led<x>i with the same <x> number. Prior to version 6.5.0.12, Tasmota only supported up to two LED components to indicate the power state of the relay(s), and the Wi-Fi/MQTT connectivity status.

    It is possible to wire in your own LED and assign it as any of the above mentioned but that's outside the scope of this article

    If only one LED is configured, it serves both purposes; the link status LED and/or the LED that indicates the power state of the relay(s). If more than one LED component is defined, Led1/Led1i will act as the Wi-Fi/MQTT status LED and the next defined LED (e.g., Led2/Led2i) will act as the LED that indicates the power state of the relay(s). This is the default behavior. Configuring a GPIO as an LEDLink/LEDLinki component changes this behavior.

    For example, on a Sonoff Basic the green LED is used as the link status LED. Once the device connects, the LED is used to indicate the relay's power status.

    Link status LED shows the network state, more specifically the Wi-Fi and MQTT connection status.

    It blinks if the device is not connected to your Wi-Fi AP and MQTT broker (if MQTT is enabled). You can change this behaviour with LedState or turn it off with SetOption31.

    Power status LED~

    Power status LED shows the power status of relay component(s). LedMask determines which relay(s) are associated with the power status LED. This behavior can be modified with the LedState command. The LED is turned off by default when the relay is OFF and turned on when the relay switches ON.

    Note

    Depending on the device design, some LEDs are connected to the same GPIO as the relay. Those cannot be independently controlled since they have to follow the relay state.

    If you have more than one LED wired independently and you want it to show the power state of the relay, you must assign an LedLink GPIO.

    PWM LED Mode~

    Using LedPwmMode you can change the LED display mode from simple on/off to a PWM controlled LED which will enable you to f.e. display a brighter LED when the relay is on and a dimmer LED when it's OFF so you can locate the switch in the dark but not have it obnoxiously bright.

    LedPwmOff and LedPwmOn control the preset LED brightness in their respective states.

    LedPower Command~

    When you use LedPower you take over control of that particular LED and it stops being linked to its corresponding relay and being its power status LED.

    LedLink / LedLinki is used to assign the link status LED. If your device does not have an LED for link status (or you want to use that LED for a different purpose), you can assign LedLink to an available free GPIO. When LedLink(i) is assigned, other LEDs are automatically linked to their corresponding relay and serve as that relay's power status LED - i.e., Led<x>(i) links to Relay<x>(i)

    ESP32 Only Features~

    PWM6+~

    ESP32 has hardware PWM support, named ledc, for up to 16 channels depending on CPU type. You can mix lights and pure PWM channels. The first 5 PWM are reserved for lights, unless SetOption15 0. For pure PWM GPIOs, you can assign any PWM number, they don't need to be continuous. For example you can use PWM 1/2/3 for a 3-channel RGB light, and PWM 6 & PWM 10 for pure PWM at the same time.

    CPU type PWM channels
    ESP32 16 channels
    ESP32-S2 8 channels
    ESP32-C3 6 channels

    Channels are assigned to GPIOs in a first-in-first-serve way and PWM GPIOs are assigned first. If ledc channels are exhausted an error will appear in logs.

    The following GPIOs use ledc PWM channels:

    GPIO type Description
    PWM or PWMi PWM 1..5 are used for lights, PWM O6..11 are general purpose PWM.
    LedPwmMode Assigns a Led GPIO to a PWM channel
    Buzzer If BuzzerPwm is used
    Backlight PWM backlighting for displays
    XCLK Used as a clock generator for webcam

    Example of PWM console output with 16 PWM assigned. By default PWM range is 0..1023.

    RESULT = {"PWM":{"PWM1":410,"PWM2":286,"PWM3":286,"PWM4":0,"PWM5":0,"PWM6":0,"PWM7":0,"PWM8":0,"PWM9":0,"PWM10":0,"PWM11":0,"PWM12":0,"PWM13":0,"PWM14":0,"PWM15":0,"PWM16":0}}
    +

    Auto-phasing of PWM~

    By default, phases of consecutive PWM are disaligned so that a PWM pulses starts when the pulse of the previous PWM channels ends. This helps in distributing over time all pulses and have a smoother effect on power supply.

    You can revert this with SetOption134 1; all phases are synces and all pulses start at the same moment.

    H-bridge~

    H-bridge is an electronic circuit that switches the polarity of a voltage applied to a load. It uses 2 PWM outputs to control the current sent to each polarity.

    When auto-phasing is enabled, you can use 2 consecutive PWM to drive a H-bridge siunce PWM phases don't overlap - under the condition that the sum of both PWM don't exceed 1023.

    You must always ensure that the sum of both PWM channels is less or equal than 1023. Values over this threshold can damage the circuit!!!

    \ No newline at end of file diff --git a/MAX7219/index.html b/MAX7219/index.html new file mode 100644 index 0000000000..9bd6c2788d --- /dev/null +++ b/MAX7219/index.html @@ -0,0 +1 @@ + MAX7219 - Tasmota

    MAX7219

    20211204_133243 20211204_133418

    To use this driver, tasmota has to be compiled with:

    #define USE_DISPLAY_MAX7219_MATRIX

    Connect the MAX7219 display module's pins to any free GPIOs of the ESP8266 or ESP32 module. VCC should be 5V. Depending on the number of used modules and the brightness, the used current can be more than 500 mA.

    Connect the 5 outgoing pins (VCC, GND, DI, CS, CLK) of the first module to the next one. With this you can connect up to 32 modules. To extend the display hieght, multiple rows are supported. Each module row starts from left to right.

    Assign the pins as follows from Tasmota's GUI:

    DIN hardware pin --> "MAX7219 DIN" CS hardware pin --> "MAX7219 CS" CLK hardware pin --> "MAX7219 CLK"

    Once the GPIO configuration is saved and the ESP8266/ESP32 module restarts, set the Display Model to 19 and Display Mode to 0

    Depending on order of the wired 8x8 matrix modules you have got a display of size pixel_width x pixel_height. The size has to be set with the commands "DisplayWidth " and "DisplayHeight "

    After the ESP8266/ESP32 module restarts again, turn ON the display with the command "Power 1"

    Now, the following "Display" commands can be used:

    DisplayText text Sends the text to the display. If the text fits into the display, it is shown in the center. Otherwise it scrolls to the left and repeats as long it is cleared or new "DisplayText" overwrites it.

    DisplayDimmer [0..100] Sets the intensity of the display.

    Power [ON|OFF] Sitches the display on or off. When "off", the display buffer is not cleared and will be shown again when after "Power ON". Other display commands are still active when off.

    DisplayClear Clears the display

    DisplayScrollDelay [0..15] // default = 0 Sets the speed of text scroll. Smaller delay = faster scrolling. The maximum scroll speed is 50ms per pixel on DisplayScrollDelay 0.

    DisplayWidth [8..256] Sets the pixel width of the display (8x number of modules in a row)

    DisplayHeight [8..256] Sets the pixel height of the display (8x number of module rows)

    DisplayClock [0|1|2] Displays a clock. Commands "DisplayClock 1" // 12 hr format "DisplayClock 2" // 24 hr format "DisplayClock 0" // turn off clock

    Constrains:~

    • Only one text row is supported
    • Only standard ascii is supported, UTF8 or escape characters are not supported yet.
    • #define USE_DISPLAY_MAX7219_MATRIX disables USE_DISPLAY_MAX7219 and USE_DISPLAY_TM1637 (made for seven segment displays), cause it is using the same pin config.
    \ No newline at end of file diff --git a/MCP230xx/index.html b/MCP230xx/index.html new file mode 100644 index 0000000000..bd96654f45 --- /dev/null +++ b/MCP230xx/index.html @@ -0,0 +1,68 @@ + MCP23008 / MCP23017 GPIO Expander - Tasmota
    Skip to content

    MCP23008 / MCP23017 GPIO Expander~

    Technical Data from the manufacturer: * Microchip MCP23008 * Microchip MCP23017

    Generally available breakout boards for the MCP23017 look similar to this:

    https://github.com/andrethomas/images/raw/master/mcp230xx/mcp23017_breakout.jpg

    The MCP23008 has 8 IO pins which the MCP230xx driver uses as D0 - D7. The MCP23017 has 16 IO pins which the MCP230xx driver uses as D0 - D15. This is visualized in the circuit diagram below but it's important to note that the MCP23017 actually differentiates between PORTA (being A0 to A7) and PORTB (being B0 to B7) - The MCP230xx driver combines the two ports in sequence to translate to pins represented as D0 through D15 for the MCP23017.

    The chip can be connected quite easily, especially if you can source the DIP version of the chip. Here's a basic outline of what a typical circuit would require to be functional:

    Manual Wiring for MCP23008 / MCP23017

    You will need to pick an I2C address in either of the above scenario's using the address mapping according to pin A0, A1, and A2 as from the datasheet as follows:

    MCP23008 / MCP23017 I2C Address Map

    Supporting modes~

    Starting with Tasmota v12.4.0.2 there are two different modes to use MCP23xxx.

    • The original approach (now called Mode 1) supports one MCP23008 or MCP23017 with many user configurable features using commands and rules.
    • The latest approach called Mode 2, supports several and mixed MCP23008, MCP23017 and MCP23S17 adding switches, buttons and relays acted on as if they were directly connected to the ESP8266 or ESP32 configured using a JSON file containing a template describing the GPIO's as used on the basic Tasmota device.

    Mode 2~

    To enable Mode 2 you will only need to add in user_config_override.h

    #define USE_MCP23XXX_DRV

    This enables the driver which in turn at restart will search for the JSON file in three possible locations:

    • if a filesystem is present it looks for file mcp23x.dat
    • if not found and rules are supported it looks for a specific rule entry like on file#mcp23x.dat do <template> endon
    • if not found and scripts are supported it looks for a specific script like -y <template>

    If no JSON file is found the driver does not claim any MCP23xxx device and if mode 1 is enabled will allow this mode to take over.

    A typical JSON template would look like {"NAME":"MCP23008 expander","BASE":0,"GPIO":[224,225,226,227,32,33,34,35]} which adds four relays and four buttons.

    The template consists of a "NAME" data pair with any description of the template, an optional "BASE" data pair selecting if either relative (0 = default) or absolute (1) button and/or switch numbering is used and a "GPIO" data pair with numbers representing the functions of the GPIO's in order from lowest I2C address GP(A)0 to highest I2C address GP(B)7 and are based on the numbers known from the base tasmota template used on the ESP8266 or ESP32.

    The following list contains the current supported functions:

    Function Code Description
    None 0 Not used
    Button1..32 B 32..63 Button to Gnd with internal pullup
    Button_n1..32 Bn 64..95 Button to Gnd without internal pullup
    Button_i1..32 Bi 96..127 Button inverted to Vcc with internal pullup
    Button_in1..32 Bin 128..159 Button inverted to Vcc without internal pullup
    Switch1..28 S 160..187 Switch to Gnd with internal pullup
    Switch_n1..28 Sn 192..219 Switch to Gnd without internal pullup
    Relay1..32 R 224..255 Relay
    Relay_i1..32 Ri 256..287 Relay inverted
    Output_Hi Oh 3840 Fixed output high
    Output_lo Ol 3872 Fixed output low

    Some example templates

                                              S3  S2  B2 B3 Oh   B1 S1    R1        R4  R2  R3  S4
    +{"NAME":"MCP23S17 Shelly Pro 4PM","GPIO":[194,193,65,66,3840,64,192,0,224,0,0,0,227,225,226,195]}
    +
    +Inverted relays and buttons                Ri1 Ri2 Ri3 Ri4 Ri5 Ri6 Ri7 Ri8 B1 B2 B3 B4 B5 B6 B7 B8
    +{"NAME":"MCP23017 A=Ri1-8, B=B1-8","GPIO":[256,257,258,259,260,261,262,263,32,33,34,35,36,37,38,39]}
    +
    +Unique inverted relays and buttons with offset 2      Ri3 Ri4 Ri5 Ri6 Ri7 Ri8 Ri9 Ri10B3 B4 B5 B6 B7 B8 B9 B10
    +{"NAME":"MCP23017 A=Ri2-10, B=B2-10","BASE":1,"GPIO":[258,259,260,261,262,263,264,265,34,35,36,37,38,39,40,41]}
    +
    +Buttons, relays, buttons and relays                         B1 B2 B3 B4 B5 B6 B7 B8 R1  R2  R3  R4  R5  R6  R7  R8  B9 B10B11B12B13B14B15B16R9  R10 R11 R12 R13 R14 R15 R16
    +{"NAME":"MCP23017 A=B1-8, B=R1-8, C=B9-16, D=R9-16","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231,40,41,42,43,44,45,46,47,232,233,234,235,236,237,238,239]}
    +

    In Mode 2 you can choose to connect the interrupt pins from the MCP230xx to native GPIOs on the ESP. You will then need to configure the used GPIOs as "MCP23xxx Int" in the Web UI. This way, upon a change in an input, the corresponding interrupt pin will gererate a flag that will be scanned at every program cycle, making the detection as fast as possible. If not using the interrupt pins the detection will be triggered every 50ms.

    Mode 1~

    You will need to define the address you are using in user_config_override.h for the driver to know on which address the MCP23008/MCP23017 is expected to be found.

    #define USE_MCP230xx_ADDR 0x20

    The MCP23008/MCP23017 chips allow for both INPUT and OUTPUT - Most of the functionality of the driver is focused on INPUT mode - especially since they allow interrupt reporting and are 5V tolerant.

    OUTPUT functionality is however available as pinmode 5 (Documented later in this Wiki) as an additional option for those who want to use the OUTPUT functionality using the Sensor29 command which consumes ~1Kbyte of flash. The driver is disabled by default in the Tasmota firmware so the only way to gain its use would be to perform a manual compilation of your own firmware.

    There are three different levels in which functionality may be enabled, in the following order, by adding these lines in user_config_override.h:

    #define USE_MCP230xx                 // Enable INPUT mode (pinmode 1 through 4)
    +#define USE_MCP230xx_OUTPUT          // Enable OUTPUT mode (pinmode 5)
    +#define USE_MCP230xx_DISPLAYOUTPUT   // Display state of OUTPUT pins on main Tasmota web interface
    +

    The ESP8266 will automatically detect whether you have connected an MCP23008 (8 input) or MCP23017 (16 input) and will provide telemetry data in accordance with how the device was configured from within the Tasmota firmware.

    If OUTPUT is enabled, telemetry data for the current state of OUTPUT pins will also be provided by telemetry.

    MCP23008 / MCP23017 Pin numbers in Tasmota compared to datasheets~

    The table below outlines how the pins of the MCP23008/MCP23017 are assigned:

    MCP23008 / MCP23017 Pin Map

    Usage of the driver~

    The MCP230xx chip (or breakout board) must be connected to the ESP8266 and the I2C pins must be configured for the module similar to the following:

    https://github.com/andrethomas/images/raw/master/mcp230xx/Generic_Config_Menu.PNG

    One that is complete you may want to confirm that the Tasmota firmware is finding your MCP23008/MCP23017 chip by sending the command through serial or MQTT:
    I2Cscan

    You should see a response giving you an address within the range of the MCP23008/MCP23017 chip (0x20 through 0x27) which may look as follows
    MQT: stat/tasmota/RESULT = {"I2CScan":"Device(s) found at 0x20"}

    If the extender is not detected, check your wiring and pin configuration.

    The configuration of MCP23008/MCP23017 by using Sensor29 commands via the Console or MQTT messages.

    In order to use the MCP23008/MCP23017, add the following two lines in your user_config_override.h as the MCP chip support is not enabled by default.

    #define USE_MCP230xx
    +#define USE_MCP230xx_ADDR 0x20
    +

    The MCP23008/MCP23017 supports I2C address range of 0x20 through 0x27. Take care that you are not using an address which is already used by another device (e.g., 0x27 is a known address for some I2C Liquid Crystal Displays).

    Device Configuration~

    The behavior of all pins on the MCP23008/MCP23017 can be reset to a specific setting/mode globally to simplify the initial configuration as follows

    Command Parameters
    Sensor29 MCP23008 / MCP23017 I2C GPIO Expander configuration
    Reset<x> = reset all pins
    x = 1..6
    1 = INPUT mode, no reporting, no pull-up
    2 = INPUT mode, report on CHANGE, pull-up enabled
    3 = INPUT mode, report on LOW, pull-up enabled
    4 = INPUT mode, report on HIGH, pull-up enabled
    5 = OUTPUT mode (if enabled by #define USE_MCP230xx_OUTPUT)
    6 = inverted OUTPUT mode (if enabled by #define USE_MCP230xx_OUTPUT)

    pin,pinmode{,intpullup\|outstate{,repmode}}
      pin = the I/O pin on the MCP230xx chip
      • 0..7 for MCP23008
      • 0..15 for the MCP23017)
      pinmode = operational mode of the pin (?, 0..5)
      • ? = query pin configuration
      • 0 = Disabled (deprecated, but will be default for previously unconfigured devices)
      • 1 = INPUT (Floating - only telemetry data will be sent according to configuration TelePeriod intervals)
      • 2 = INPUT with INTERRUPT on CHANGE (will send an MQTT output on state change from LOW to HIGH and HIGH to LOW)
      • 3 = INPUT with INTERRUPT on CHANGE to LOW (will send an MQTT output on state change only from HIGH to LOW)
      • 4 = INPUT with INTERRUPT on CHANGE to HIGH (will send an MQTT output on state change only from LOW to HIGH)
      • 5 = OUTPUT (if enabled with #define USE_MCP230xx_OUTPUT)
      • 6 = inverted OUTPUT (if enabled with #define USE_MCP230xx_OUTPUT)
      intpullup (pinmode 1..4). Pull-up resistors are disabled by default for pin mode 1 whilst enabled by default for pin modes 2..4 (because they are interrupt enabled pins and we do not want signal bounce). The internal pull-up on these pins may be disabled if necessary if you are biasing them externally.
      • 0 = weak internal pull-up disabled (default for pinmode 1)
      • 1 = weak internal pull-up enabled (default for pinmode 2..4)
      outstate (pinmode 5..6) = set the default state of an OUTPUT pin on reset/power-up. If your device is configured to save state (SetOption0 = 1), the outstate setting will be ignored and the last known state of the pin will be applied during power-up/reset.
      • 0/off = set output pin to OFF
      • 1/on = set output pin state to ON
      • 2/device = keep the value stored on the MCP230xx device
      repmode = reporting mode (optional). Applicable only for pinmode 2..4. Reporting mode is disabled for pinmode 1 and for output pinmodes (5..6)
      • 0 = interrupt using Event and report using telemetry (default)
      • 1 = interrupt using Event only (no telemetry reported)
      • 2 = report using telemetry only (no Event triggered)

      Examples:
      Sensor29 Reset1
      MQT: stat/tasmota/RESULT = {"Sensor29_D99":{"MODE":1,"PULL_UP":"OFF","INT_MODE":"DISABLED","STATE":""}}
      Pin and State is reported as 99 because it is set across all pins.
      Mode should correspond with the reset pinmode option used.

      Sensor29 0,?
      MQT: stat/tasmota/RESULT = {"Sensor29_D0":{"MODE":1,"PULL_UP":"OFF","INT_MODE":"DISABLED","STATE":"ON"}}
      Confirming that the pin is in pinmode 1 and that the pull-up resistor is not enabled.
      INT_MODE indicates the interrupt mode for pins which are interrupt enabled (pinmode 2 through 4) - In the example above it is disabled for pin mode 1 (INPUT without INTERRUPT)
      The current STATE of the pin as ON or OFF is reported as at the time the command is issued is also reported.

      IMPORTANT NOTICE ON USE OF INTERRUPTS~

      Only use interrupts on pins which are either explicitly pulled down GND or up to VCC externally as floating pins may cause unintended MQTT responses for pins which are floating. So unless your connected wire/device explicitly pulls the pin to GND or VCC only when conditions of an interrupt would be met it is recommended that you either do not set a pin for an interrupt mode or at least enable pull-up resistors for the unused pins with pullup = 1 when you perform your sensor29 pin,pinmode,pullup command.

      Examples of some pin configuration options:

      sensor29 4,1,0 - Will enable D4 for INPUT without internal pull-up resistor

      sensor29 3,1,1 - Will enable D3 for INPUT with the internal pull-up resistor ENABLED

      sensor29 5,2,1 - Will enable D5 for INPUT and report on change state from LOW to HIGH and HIGH to LOW via MQTT

      sensor29 6,3,1 - Will enable D6 for INPUT and report on change state from HIGH to LOW (note pull-up is also enabled)

      sensor29 2,4,0 - Will enable D2 for INPUT and report on change state from LOW to HIGH (note pull-up is not enabled)

      Pull-up resistor support is valid for all modes from 1 through 4

      Default telemetry logging will occur for all pins as per the configured logging interval of the ESP8266 as configured in the Tasmota firmware options. The telemetry logging will push out to log and MQTT a JSON as follows:

      tele/tasmota/SENSOR = {"Time":"2018-08-18T16:13:47","MCP230XX": "D0":0,"D1":0,"D2":1,"D3":0,"D4":0,"D5":0,"D6":0,"D7":1}}
      +

      Again, this will depend on whether an MCP23008 or MCP23017 is used insofar that the number of pins/bits reported will be 8 (0 to 7) or 16 (0 to 15) respectively.

      INTERRUPT MODES AND USAGE~

      Interrupts will report for individual pins as and when the conditions which were configured are met and will look something like this:

      Interrupt message on HIGH for input pin 0

      MQT: stat/tasmota/RESULT = {"Time":"2018-08-19T16:04:50","MCP230XX_INT":{"D0":1,"MS":301}}
      +
      Interrupt message on LOW for input pin 1
      MQT: stat/tasmota/RESULT = {"Time":"2018-08-19T16:04:50","MCP230XX_INT":{"D1":0,"MS":519}}
      +

      The state of the pin captured during the interrupt is reported as Dx=y where x is the pin number and y is the state of the pin. In addition the number of milliseconds since the last interrupt occurred for the particular pin is also reported as MS=xx where xx is the number of milliseconds recorded.

      In addition to the MQTT message the driver will also execute an event command in the following format:

      event MCPINT_Dxx=y

      Where xx = the pin number from 0 through 7 (MCP23008) or 0 through 15 (MCP23017) and y the state of the pin as it was captured by the interrupt register of the MCP23008/MCP23017 chip.

      The complete output for an interrupt enabled pin would look like this:

      MQT: stat/tasmota/RESULT = {"Time":"2018-08-19T16:08:28","MCP230XX_INT":{"D0":0,"MS":217353}}
      +SRC: Rule
      +RSL: Group 0, Index 1, Command EVENT, Data MCPINT_D0=0
      +MQT: stat/tasmota/RESULT = {"Event":"Done"}
      +
      MQT: stat/tasmota/RESULT = {"Time":"2018-08-19T16:08:46","MCP230XX_INT":{"D0":1,"MS":18101}}
      +SRC: Rule
      +RSL: Group 0, Index 1, Command EVENT, Data MCPINT_D0=1
      +MQT: stat/tasmota/RESULT = {"Event":"Done"}
      +

      The latter makes it possible to integrate interrupt responses with rules for example:

      rule on event#MCPINT_D0=1 do power on endon on event#MCPINT_D0=0 do power off endon
      +

      In the example above the rule would respond to an interrupt of HIGH on pin 0 of the MCP by executing command "power on" and respond to an interrupt of LOW on pin 0 with the command "power off"

      See the Wiki on Using Rules for more information on how this can be helpful to your requirements.

      If you require only one of the two reporting methods you may use the sensor29 command to configure the interrupt behavior according to your requirements using command:

      sensor29 pin,pinmode,pullup,intmode

      The intmode parameter is optional for pin modes 2 through 4 (those that support interrupts) and may be configured according to the table below depending on your requirements:

      MCP23008 / MCP23017 Interrupt Modes

      Keep in mind that the MCP23008/MCP23017 chip will only store the last interrupt registered in the interrupt register and capture register - Because the interrupt register is only checked every 50 milliseconds by the Tasmota firmware you may experience missed interrupts if your incoming signals fluctuate/change faster than 20 times per second.

      HOME ASSISTANT TIPS~

      You can use SetOption59 1 in order to get extra SENSOR status telemetry messages in addition to event-triggered RESULT messages. This allows very good integration with Home Assistant because it needs to monitor only one payload for both periodic and instant messages using binary_sensor:

      - platform: mqtt
      +  name: "MCP23017 Teszt D0 SENSOR"
      +  state_topic: "tele/tasmota/SENSOR"
      +  value_template: "{{ value_json['MCP230XX'].D0 }}"
      +  payload_on: "1"
      +  payload_off: "0"
      +  availability_topic: "tele/tasmota/LWT"
      +  payload_available: "Online"
      +  payload_not_available: "Offline"
      +  qos: 0
      +  device_class: door
      +

      ADVANCED FUNCTIONS~

      Several advanced functions have been added to extend the flexibility and interoperability of the MCP23008/MCP23017 with specific focus on adding functionality which is not present on the hardware's built-in GPIO pins and offloading some of the functionality that would normally be performed by rules or counters on the Tasmota device into the driver of the MCP23008/MCP23017.

      These include the following * INTPRI - Interrupt Priority, being able to control the rate at which the MCP23008/MCP23017 is polled to see if any interrupts has occurred since the previous poll. * INTDEF - Interrupt Deffer, being able to control the number of interrupts that are ignored on a specific pin before reporting would occur via telemetry and/or EVENT. * INTTIMER - Interrupt Timer which allows for time based counter reporting, specifically reporting the number of times an interrupt has occurred on interrupt enabled pins. * INTCNT - Works with INTTIMER to enable/disable counting for a specific pin. * INTRETAIN - Keep track of whether an interrupt occurred or not and defer reporting to next telemetry message.

      The above additions are described in further detail below.

      ADVANCED FUNCTION #1 - INTERRUPT PRIORITY (INTPRI)~

      The maximum interrupt polling rate is once per approximately 50 milliseconds - This is what the Tasmota firmware allows as a maximum and how it is configured in the MCP23008/MCP23017 driver by default.

      If you want to reduce the number of interrupt polls per second you may use the INTPRI command parameter as follows:

      sensor29 intpri

      Will give you the current setting via JSON response as follows:

      MQT: stat/tasmota/RESULT = {"MCP230xx_INTPRI":{"D_99":0}}

      To change the value you may use command as follows:

      sensor29 intpri,x

      Where x is the number of 50ms cycles (between 0 and 20) which will be skipped before the MCP23008/MCP23017 chip is polled for interrupt. The last interrupt recorded by the MCP23008/MCP23017 will be reported via the configured method.

      For example, lets assume you only want the interrupt polling to occur every 500ms (i.e. twice per second) you could do command:

      sensor29 intpri,10 // interrupt polled every 10*50 milliseconds, approximated
      +

      ADVANCED FUNCTION #2 - INTERRUPT DEFER (INTDEF)~

      This setting is useful if you need to defer the reporting of an interrupt by event or telemetry until it has occurred at least X number of times.

      Syntax:

      sensor29 intdef,pin         // Will provide current setting of pin
      +sensor29 intdef,pin,x       // Will set new deffer value to x (0-15)
      +

      Examples:

      sensor29 intdef,pin,5       // Will only report interrupt when it occurs 5 times
      +sensor29 intdef,pin,10      // Will only report interrupt when it has occurred 10 times
      +

      Interrupts occurring a number of times prior to the setting will be counted but ignored for reporting purposes.

      ADVANCED FUNCTION #3 - INTERRUPT TIMER (INTTIMER)~

      This function is used in conjunction with INTCOUNT (Documented below)

      It allows a timer to be configured over which period the number of interrupts will be counted.

      Syntax:

      sensor29 inttimer          // Will provide the current amount of seconds for timer
      +sensor29 inttimer,x        // Allows setting number of seconds (x) for timer interval
      +

      ADVANCED FUNCTION #4 - INTERRUPT COUNTER ENABLE (INTCNT)~

      Enable interrupt counting for a particular pin. This functionality works in conjunction with INTTIMER (Documented above)

      Syntax:

      sensor29 intcnt,pin       // Readback current setting of interrupt counting for pin (0=OFF/1=ON)
      +sensor29 intcnt,pin,x     // Enable/Disable interrupt counting for pin (x=0=OFF,x=1=ON)
      +

      Use case example could be if you want to count the number of times an interrupt occurred on a D0 over a period of 60 seconds. For this we will need the following:

      sensor29 inttimer,60      // Enable interrupt timer for 60 second interval
      +sensor29 intcnt,0,1       // Enable interrupt counter for pin D0
      +

      The above will result in the number of interrupts that occur within the 60 second period configured to be counted and then reported via telemetry at the end of the 60 second time.

      A use case for this would be to determine the RPM of something, or perhaps the number of pulses received from an energy meter within a 60 second period to determine energy usage on a per minute bases... or wind speed from impulses received from an anemometer.

      ADVANCED FUNCTION #5 - INTERRUPT RETAIN (INTRETAIN)~

      This functionality disables immediate even and/or telemetry reporting for a specific pin that has been configured for any of the interrupt modes listed above.

      If this is enabled for a particular pin and the pin has an interrupt mode configured the fact that an interrupt condition was met will be remembered (but not reported immediately) and will be reported in a MQTT message when the next telemetry period occurs in the following format:

      {"Time":"2018-12-06T23:59:26","MCP_INTRETAIN": {"D0":1,"D1":0,"D2":1,"D3":1,"D4":0,"Value":13}}
      +

      In the example above it means that an interrupt occurred at some point during the previous telemetry period for pins D0, D2, and D3 as indicated by the 1's present for each pin - Pins with a value of 0 means that although the pin was configured for interrupt retain that no interrupt occurred during the previous telemetry period for that particular pin.

      For the sake of handling bit-wise operations within your home automation software the decimal value of the respective bits are also aggregated into the Value output included in the telemetry message.

      Syntax:

      sensor29 intretain,pin       // Readback current setting of interrupt retain for a pin (0=OFF/1=ON)
      +sensor29 intretain,pin,x     // Enable/Disable interrupt counting for pin (x=0=OFF,x=1=ON)
      +


      OUTPUT FUNCTIONS (PIN MODES 5 AND 6)~

      Enable OUTPUT support by removing the comment (#) for the following compiler directive to your user_config_override.h

      #define USE_MCP230xx_OUTPUT

      This will extend the sensor29 command enabling pinmode 5 and 6 (inverted) for output, for example (nb. the output state will only be used/set with setoption0 = 0):

      sensor29 0,5,0  // Configure pin 0 as OUTPUT and default to OFF on reset/power-up
      +sensor29 0,5,1  // Configure pin 0 as OUTPUT and default to ON on reset/power-up
      +sensor29 0,5,2  // Configure pin 0 as OUTPUT and default to the value read from the MCP230xx on reset/power-up
      +sensor29 0,6,0  // Configure pin 0 as INVERTED OUTPUT and default to ON on reset/power-up
      +sensor29 0,6,1  // Configure pin 0 as INVERTED OUTPUT and default to OFF on reset/power-up
      +sensor29 0,6,2  // Configure pin 0 as INVERTED OUTPUT and default to the value read from the MCP230xx on reset/power-up
      +

      Confirmation will be sent using MQT, for example:

      MQT: stat/tasmota/RESULT = {"Sensor29_D2":{"MODE":5,"START_UP":"OFF","STATE":"OFF"}}
      +

      The only difference between pinmode 5 and pinmode 6 is that pinmode 5 will result in normal output state, i.e. pin will be LOW when OFF whereas pinmode 6 will cause the pin to be HIGH when OFF. This is useful when using relays which have inverted inputs.

      If SAVE_STATE / setoption0 is enabled in your firmware configuration then the last known state of the pin will be used on power-up/reset thereby ignoring the pull-up parameter in the commands above.

      To change the state of an output pin you may use:

      sensor29 0,ON   // Turn pin ON (HIGH if pinmode 5 or LOW if pinmode 6(inverted))
      +sensor29 0,OFF  // Turn pin OFF (LOW if pinmode 5 or HIGH if pinmode 6(inverted))
      +sensor29 0,T    // Toggle the current state of pin from ON to OFF, or OFF to ON
      +

      Additionally all OUTPUT pins will be exposed as RELAYS and ordered behind the normal GPIO based RELAYS. Instead of the above sensor command you can also use the POWERxx command like for any RELAY. If you define INTERLOCK and/or INTERLOCK groups these will also take care about the out pins. The numbering of the RELAY's is following the standard tasmota behavior. Counting from D0 any defined OUT pin will add a new RELAY. Example: D0, D2, D3, D7 are out pins, then D0=POWER1, D2=POWER2, D3=POWER3 and D7=POWER4. Same behavior you can expect when defining PULSETIME for RELAYS.

      Telemetry response will be provided accordingly, for example:

      MQT: stat/tasmota/RESULT = {"S29cmnd_D0":{"COMMAND":"ON","STATE":"ON"}}
      +MQT: stat/tasmota/RESULT = {"S29cmnd_D0":{"COMMAND":"OFF","STATE":"OFF"}}
      +MQT: stat/tasmota/RESULT = {"S29cmnd_D0":{"COMMAND":"TOGGLE","STATE":"ON"}}
      +

      COMMAND = Command which was sent

      STATE = New state after execution of command

      Telemetry data is provided for pins which are enabled for output. For example, if pin 0 was enabled for OUTPUT the following additional telemetry message will be sent by MQTT at the same time as the normal telemetry interval occurs which reports the current states of pins. Additionally you can also use the standard POWERxx reporting.

      MQT: tele/tasmota/SENSOR = {"Time":"2018-08-18T16:41:20","MCP230XX":{"D0":0,"D1":0,"D2":1,"D3":0,"D4":0,"D5":0,"D6":0,"D7":0},"MCP230_OUT": {"OUT_D4":"OFF","END":1}}
      +

      Note the MCP230XX telemetry which provides the current logic state of all the pins and then the second MQT telemetry as MCP230_OUT which indicates the current state of pins configured for OUTPUT - In this case pin 4 or D4

      Remember to adhere to the current limitations of OUTPUT pins when using the device for switching external devices such as LED's. That being said most readily available relay pc boards available from vendors are optically isolated from the input so these will work perfectly.

      \ No newline at end of file diff --git a/MCP9808/index.html b/MCP9808/index.html new file mode 100644 index 0000000000..a7f1cab51a --- /dev/null +++ b/MCP9808/index.html @@ -0,0 +1,5 @@ + MCP9808 temperature sensor - Tasmota
      Skip to content

      MCP9808 temperature sensor~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_MCP9808
      +#define USE_MCP9808      // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code)
      +#endif
      +

      the MCP9808 is an I2C temperature sensor. Tasmota support up to 8 devices.

      Configuration~

      Wiring~

      MCP9808 ESP
      VCC 3.3V
      GND GND
      SDA GPIOx
      SCL GPIOy

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. GPIOx to I2C SDA
      2. GPIOy to I2C SCL

      After a reboot the driver will detect automatically up to 8 MCP9808 and display sensor readings.

      and in MQTT topic (according to TelePeriod):

      {"Time":"2020-01-01T00:00:00","MCP9808":{"Temperature":24.7},"TempUnit":"C"}
      +

      Breakout Boards~

      MCP9808

      Datasheet~

      MCP9808

      \ No newline at end of file diff --git a/MFRC522/index.html b/MFRC522/index.html new file mode 100644 index 0000000000..880a3b95ae --- /dev/null +++ b/MFRC522/index.html @@ -0,0 +1,8 @@ + MFRC522 RFID reader - Tasmota
      Skip to content

      MFRC522 RFID reader~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #define USE_SPI                         // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC)
      +#define USE_RC522                       // Add support for MFRC522 13.56Mhz Rfid reader (+6k code)
      +#define USE_RC522_DATA_FUNCTION         // Add support for reading data block content (+0k4 code)
      +#define USE_RC522_TYPE_INFORMATION      // Add support for showing card type (+0k4 code)
      +

      The MFRC522 is a highly integrated reader/writer IC for contactless communication at 13.56 MHz. The MFRC522 reader supports ISO/IEC 14443 A/MIFARE and NTAG. Datasheet.

      This reader is ubiquitous in many Arduino starter and sensor kits. It uses SPI protocol for communication with ESP.

      Wiring~

      MFRC522 ESP8266 Tasmota
      SDA GPIO0..5,15,16 RC522 CS
      SCK GPIO14 SPI CLK
      MOSI GPIO13 SPI MOSI
      MISO GPIO12 SPI MISO
      IRQ not used
      GND GND
      RST GPIO0..5,15,16 RC522 Rst
      3V3 3V3

      Warning : on esp8266, as the RST pin is pulling the signal high by default, gpio15 cannot be used for that signal. Please check esp8266 gpios specifications or the table in the section GPIO Overview

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      • GPIOx to RC522 Rst
      • GPIOy to RC522 CS
      • GPIO12 to SPI MISO
      • GPIO13 to SPI MOSI
      • GPIO14 to SPI CLK

      The module will reboot when you save this configuration.

      During start-up the following information should be visible in your console output:

      00:00:00 MFR: RC522 Rfid Reader detected
      +
      If the device was not found please check your wiring and configuration and confirm that everything is as it should be.

      Example

      Configured using NodeMCU on pins: D1 (connected to RC522 Rst) and D8 (connected to RC522 CS)

      MFRC522 Module Configuration

      Usage~

      Tasmota will scan for a new card detect 4 times per second and if found will report it via immediate telemetry.

      The output on the console will look similar to the below when a new card is detected

      13:10:50.346 MQT: tele/rfid-test/SENSOR = {"Time":"2021-01-23T13:10:50","RC522":{"UID":"BA839D07","Data":"","Type":"MIFARE 1KB"}}
      +

      The UID of the card/tag is reported and any text stored in BLOCK 1 of a Mifare Classic card (up to 15 characters in length) is reported in the DATA field of the JSON sent via telemetry. Please note that the DATA field cannot contain spaces.

      Using the UID or DATA~

      For the purpose of using card/tag data on the device itself you will need to use rules along with the events that are caused.

      Example

      Example rule for responding to a specific UID on the device when a card/tag matching a specific UID is presented

      rule1 on RC522#UID=BA839D07 do power on endon
      +

      Breakout Boards~

      MFRC522 Module Configuration

      \ No newline at end of file diff --git a/MGC3130/index.html b/MGC3130/index.html new file mode 100644 index 0000000000..a04dcff018 --- /dev/null +++ b/MGC3130/index.html @@ -0,0 +1,4 @@ + MGC3130 3D tracking and gesture controller - Tasmota
      Skip to content

      MGC3130 3D tracking and gesture controller~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_MGC3130
      +#define USE_MGC3130     // [I2cDriver27] Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem)
      +#endif
      +

      The MGC3130 is an electrical-field (E-field) based three-dimensional (3D) tracking and gesture controller from Microchip. There are several boards available, which are connected via I2C.

      Usage~

      • wire up SDA and SDL and configure as usual in Tasmota
      • wire up RESET and TRFR (names may vary on different boards) to free GPIO's and configure them accordingly (TRFR is GPIO: MGC3130_XFER)

      Expected behaviour~

      • after boot gestures (FLICK, EDGE_FLICK, TOUCH, TAP, DOUBLE_TAP) will be sensed and published via MQTT

      Flicks:

      Taps:

      • you can select different modes with the COMMANDS, touch will always be sensed and report the duration in 1/20 seconds.

      Touch:

      • the airwheel gesture will be sensed and published as "AW" via MQTT with values between 0 and 1023 - clockwise up

      Airwheel:

      • after entering position mode the values for x,y,z will be sensed and published via MQTT with values between 0 and 1023 for x,y. Data is only published, when z is in the upper half (z values are between 0 and 511).

      Location sensing in active volume:

      • near the surface you can move your finger without producing location data, i.e. to change the mode.

      • at the moment the circle gestures ((COUNTER)CLOCKWISE) must be activated with the COMMAND: SENSOR91 1 (we must wait 250ms after the start and can not activate it in the init function, this might differ depending on the board firmware)

      Commands~

      • SENSOR36 0 - next mode
      • SENSOR36 1 - gesture mode
      • SENSOR36 2 - air wheel mode
      • SENSOR36 3 - position mode (ATTENTION: this will send a lot of data!)

      MQTT Messages~

      • The terminology follows the documentation from MICROCHIP, but is shortened to save space especially for RULES. The board is to be viewed like a map, where UP is NORTH and RIGHT is EAST and so on. The big central part of the surface is CENTRE.
      • A movement (FLICK) from LEFT to RIGHT is translated to FLICK-WEST-EAST and then finally shortened to {FL_WE:1}.
      • If you only do a flick at the left edge (with a short movement to the right) this will be EDGE-FLICK-WEST-EAST and then in the final message: {E_FL_WE:1}.
      • A TOUCH (leaving your finger on the board) reports the location and the duration (in counts of 50ms). So touching the center for a second, publishes messages from {TH_C:1} up to (approx.) {TH_C:20}.
      • Touching for less than 1,25 seconds triggers a TAP-message in the moment of leaving the surface, so on the lower edge we will see: {TP_S:1} (plus some {TH_S:1...x} for the contact duration).
      • Double Tap (like a double click on your mouse) works similar, but will (at the moment) always trigger a (single) TAP at the same position. Example for the upper edge: {DT_N:1} and before that: {TP_N:1} and some {TH_N:1...x}.
      • Airwheel is a circular finger movement above the sensor, which will trigger a message {AW:0 ... 1023}.
      • In position mode we get {X:0...1023,Y:0...1023,Z:0...511}, where X and Y are 0 in the lower left (west-south) corner. The z value is not published in close proximity to the surface in order to have a chance to switch to another mode with a TOUCH, TAP or DOUBLE-TAP. So the active sensing volume is in the upper z-half of the theoretical maximum sensing volume.

      Examples~

      A possible solution to cycle through the modes only with the sensor by double tapping the center is using rules:
      rule1 on Tele-MGC3130#DT_C do sensor91 0 endon or with a "long" touch of a second rule1 on Tele-MGC3130#TH_C > 20 do sensor91 0 endon

      Considerations~

      This is an extremely versatile sensor and the main problem is not to get it to work somehow in TASMOTA, but to make it usable in a sensible way. We can measure and publish all kinds of data in parallel, but this will likely end up in an unusable situation.
      It is important to have a basic understanding of the sensor, to not get confused with seemingly unreasonable messages (DOUBLE TAP triggers a TOUCH (or more than one), then a TAP (after the first lift of the finger) and then a DOUBLE TAP.
      The naming conventions of the gestures are according to the data sheets from Microchip, because if we only would have simple FLICKS, it would have made it easy to use: UP, DOWN, LEFT, RIGHT. But we have EDGE FLICKS and various TOUCHES too, and so the direction name could be ambiguous. That's why we (have to) use NORTH-SOUTH, EAST-WEST ... and NORTH, SOUTH, .... and CENTRE.
      To make the MQTT messages not too long, some useful abbreviations have to be found. This is definitely work in progress.

      Known Issues~

      After the initial flashing the ESP8266/TASMOTA can freeze at startup with a connected sensor board. If this happens disconnect the wires from the MGC3130-board (I2C-wires should be enough), reboot and reconnect.
      Keep in mind, that in general many things will interfere with an electrical field. In certain places it can simply be impossible to use such kind of sensor. For testing reasons it is fine to connect the ESP8266-device to the USB-Port of a computer (for POWER and SERIAL), but this will likely lead to weird effects in some cases (i.e. reading of nonsense-location-data by simply touching the keyboard of a connected laptop).
      The chip can saturate the I2C-bus and additional I2C-devices can lead to problems. The driver was developed and tested on a SKYWRITER-board from PIMORONI. The behavior of other boards can not be guaranteed.

      \ No newline at end of file diff --git a/MH-Z19B/index.html b/MH-Z19B/index.html new file mode 100644 index 0000000000..79358a8a95 --- /dev/null +++ b/MH-Z19B/index.html @@ -0,0 +1,4 @@ + MH-Z19 CO2 Sensor - Tasmota
      Skip to content

      MH-Z19 CO2 Sensor~

      This feature is included only in tasmota-sensors and tasmota32 binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_MHZ19 
      +#define USE_MHZ19       // Add support for MH-Z19 CO2 sensor (+2k code)
      +#endif
      +

      The MH-Z19 sensor is manufactured by Winsen Lt., China and the measurement method used is based on the non-dispersive infrared (NDIR) principle to detect the existence of CO2 in the air.

      Key features according to the manufacturer are:

      • good sensitivity
      • non-oxygen dependent
      • long life
      • built-in temperature compensation
      • UART serial interface and Pulse Width Modulation (PWM) output

      Principle of operation:
      The main components of an NDIR sensor are an infrared source (lamp), a sample chamber or light tube, a light filter and an infrared detector. The IR light is directed through the sample chamber towards the detector. In parallel there is another chamber with an enclosed reference gas, typically nitrogen. The gas in the sample chamber causes absorption of specific wavelengths according to the BeerñLambert law, and the attenuation of these wavelengths is measured by the detector to determine the gas concentration. The detector has an optical filter in front of it that eliminates all light except the wavelength that the selected gas molecules can absorb.

      Wiring~

      ESP MH-Z19
      VCC +5V Vin
      GND GND
      TX Rx
      RX Tx

      In some situations if you only get 0 ppm displayed it may be necessary to set "TX GPIO1" to "MHZ Rx" and "RX GPIO3" to "MHZ Tx" and correspondingly reverse the cabling for RX/TX. See here for more details.

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. RX to MHZ Tx
      2. TX to MHZ Rx

      After a reboot the driver will detect MH-Z19 automatically and display measurements.

      Measure range can be selected with command:

      • sensor15 1000 for 1000 ppm range
      • sensor15 2000 for 2000 ppm range
      • sensor15 3000 for 3000 ppm range
      • sensor15 5000 for 5000 ppm range

      Full commands list for the sensor

      Model Comparison~

      Product model MH-Z19B MH-Z19C MH-Z19D
      Power supply voltage 4.5~ 5.5 V DC DC (5.0 ± 0.1) V DC (5.0 ± 0.1) V
      Average current < 60mA(@5V) <40mA (@5V power supply) <40mA (@5V power supply)
      Peak current 150 mA (@5V supply) 125mA (@5V power supply) 125 mA (@5V power supply)
      Measuring range 0~5000ppm, 0~10000ppm 4005000ppm(optional)
      400
      10000ppm range could be customized
      400~10000ppm(optional)
      Warm-up time 3min 2.5min 1min
      Response time T90<120s T90< 120s T90 <120s
      Working temperature 0 ~ 50 ℃ -10℃ ~ 50℃ -10℃ ~ 50℃
      Working humidity 0~ 90% RH (No condensation) 0~ 90% RH (No condensation) 0~ 90% RH (No condensation)
      Storage temperature -20℃~60℃ -20℃~60℃
      Source link link link

      Available from AliExpress

      \ No newline at end of file diff --git a/MLX90614/index.html b/MLX90614/index.html new file mode 100644 index 0000000000..10b8994365 --- /dev/null +++ b/MLX90614/index.html @@ -0,0 +1,10 @@ + MLX90614 infrared thermometer - Tasmota
      Skip to content

      MLX90614 infrared thermometer~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_MLX90614
      +#define USE_MLX90614            // [I2cDriver32] Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code)
      +#endif
      +

      The MLX90614 is an infrared thermometer for non-contact temperature measurements.

      Configuration~

      Wiring~

      MLX90614 ESP
      GND GND
      VCC 3.3V
      SDA GPIOx
      SCL GPIOy

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. GPIOx to I2C SDA
      2. GPIOy to I2C SCL

      After a reboot the driver will detect MLX90614 automatically and display Temperature measurements.

      MLX90614 WebUi Display

      Sensor sends a tele/%topic%/SENSOR JSON response:

      tele/tasmota/SENSOR = {"Time":"2019-11-11T00:03:30","MLX90614":{"OBJTMP":23.8,"AMBTMP":22.7}}
      +

      Breakout Boards~

      MLX90615~

      This driver will also work with MLX90615 sensor with some code alterations.

      Change lines 26-30

      #define I2_ADR_IRT      0x5b
      +
      +#define MLX90614_RAWIR1 0x05
      +#define MLX90614_TA     0x26
      +#define MLX90614_TOBJ1  0x27
      +

      \ No newline at end of file diff --git a/MLX90640/index.html b/MLX90640/index.html new file mode 100644 index 0000000000..b418e482fa --- /dev/null +++ b/MLX90640/index.html @@ -0,0 +1,11 @@ + MLX90640 Far infrared thermal sensor array - Tasmota
      Skip to content

      MLX90640 Far infrared thermal sensor array~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_MLX90640
      +#define USE_MLX90640        // [I2cDriver53] Enable MLX90640 IR array temperature sensor (I2C address 0x33) (+20k code)
      +#endif
      +

      The MLX90640 is an IR sensor array of 32x24 pixels, that is connected via a usual I2C-connection. Intended use case is to measure multiple points of interest of an object without the need to directly put a sensor on it, for instance if you are not allowed to apply hardware modifications to a heating. The update frequency of the measurements is approximately 1 per second. In the first driver version 6 POI‘s are freely selectable (POI1..6). POI-0 is reserved for the ambient temperature of the sensor (Ta).
      Data is published at Teleperiod as an array [POI-0...6]:

      {
      +    "Time": "2020-09-11T09:18:08",
      +    "MLX90640": {
      +        "Temperature": [30.8, 28.5, 24.2, 25.7, 24.5, 24.6, 24.9]
      +    },
      +    "TempUnit": "C"
      +}
      +

      Configuration~

      Wiring~

      MLX90640 ESP
      GND GND
      VCC 3.3V
      SDA GPIOx
      SCL GPIOy

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. GPIOx to I2C SDA
      2. GPIOy to I2C SCL

      After a reboot the driver will detect MLX90614 automatically and display Temperature measurements.

      Commands~

      Command:
      MLXPOIn xxyy, n ist poi 1...6, xx 0...31, yy 0...23
      Example: Point 2 at x:12 and y:08 results in mlxpoi2 1208

      The configuration can be saved via RULES.

      Web-GUI~

      In order to find the correct coordinates a thermal view is provided via webGUI, where points can be validated and/or updated. A stable Wifi-setup is mandatory for reliable operation.

      Breakout Boards~

      \ No newline at end of file diff --git a/MPR121/index.html b/MPR121/index.html new file mode 100644 index 0000000000..e92b29de6e --- /dev/null +++ b/MPR121/index.html @@ -0,0 +1,4 @@ + MPR121 capacitive touch sensor - Tasmota
      Skip to content

      MPR121 capacitive touch sensor~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_MPR121
      +#define USE_MPR121      // [I2cDriver23] Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code)
      +#endif
      +

      • 12-button or touch pad I2C controller
      • Driver supports up to 4x MPR121 controllers (= 48 buttons per ESP8266)
      • Electrodes can be covered with adhesive paper and adhesive plastic foil for labeling and symbols
      • Break-out and touch button PCBs ~ 2€ at AliExpress
      • Break-out PCB ~ $8 at Adafruit

      Technical Data from the manufacturer~

      Wiring breakout boards~

      Breakout ESP
      VCC/VIN +3.3VDC
      GND GND
      SCL GPIO I2 SCL
      SDA GPIO I2 SDA
      IRQ NC

      I2C address selection~

      Connect the ADD(R) pin/pad to one of the following pins/pads:

      Address ADD(R)
      0x5A NC
      0x5B +3.3VDC
      0x5C GPIO I2 SDA
      0x5D GPIO I2 SCL

      Tasmota SettingsCompile Tasmota with #define USE_MPR121 added in user_config_override.h~

      The driver will detect the I2C addresses of the MPR121s automatically. The MPR121 chip (or breakout board) must be connected to the ESP8266 and the I2C GPIO pins must be configured:

      I<sup>2</sup>C GPIO configuration

      Generally available types of breakout boards~

      Adafruit 12-Key Capacitive Touch Sensor Breakout - MPR121 Touchpad front Touchpad back Break-out front Breakout back

      \ No newline at end of file diff --git a/MPU-6050/index.html b/MPU-6050/index.html new file mode 100644 index 0000000000..b9706d8ef8 --- /dev/null +++ b/MPU-6050/index.html @@ -0,0 +1,20 @@ + MPU-6050 gyroscope and accelerometer - Tasmota
      Skip to content

      MPU-6050 gyroscope and accelerometer~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_MPU6050
      +#define USE_MPU6050                // [I2cDriver25] Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+3K3 of code and 188 Bytes of RAM)
      +  #define USE_MPU6050_DMP          // Enable in MPU6050 to use the DMP on the chip, should create better results (+8k6 of code)
      +#endif
      +

      The MPU-6050 combines a 3-axis gyroscope and a 3-axis accelerometer on the same silicon die, together with an onboard Digital Motion Processor™ (DMP™), which processes complex 6-axis MotionFusion algorithms.

      Datasheet

      Configuration~

      Wiring~

      MPU-6050 ESP
      GND GND
      VCC 3.3V
      SDA GPIOx
      SCL GPIOy
      XDA
      XCL
      AD0
      INT

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. GPIOx to I2C SDA
      2. GPIOy to I2C SCL

      After a reboot the driver will detect MCU-69050 automatically and display measurements.

      MLX90614 WebUi Display

      Sensor sends a tele/%topic%/SENSOR JSON response:

      tele/tasmota/SENSOR = {"Time":"2019-12-10T19:37:50","MPU6050":{"Temperature":27.7,"AccelXAxis":-7568.00,"AccelYAxis":-776.00,"AccelZAxis":12812.00,"GyroXAxis":270.00,"GyroYAxis":-741.00,"GyroZAxis":700.00},"TempUnit":"C"}
      +

      Digital Motion Processor Enabled~

      If you define flag

        #define USE_MPU6050_DMP          // Enable in MPU6050 to use the DMP on the chip, should create better results (+8k6 of code)
      +
      enabling Digital Motion Processor™ (DMP™) you will get additional Yaw, Pitch and Roll measurements and more accurate measurements overall

      MLX90614 WebUi Display

      Sensor sends an expanded tele/%topic%/SENSOR JSON response:

      tele/tasmota/SENSOR = {"Time":"2019-12-10T19:24:55","MPU6050":{"Temperature":25.0,"AccelXAxis":756.00,"AccelYAxis":-1409.00,"AccelZAxis":2152.00,"GyroXAxis":0.00,"GyroYAxis":-1.00,"GyroZAxis":10.00,"Yaw":0.86,"Pitch":-1.45,"Roll":-10.76},"TempUnit":"C"}
      +

      Breakout Boards~

      GY-521

      Rule Triggers~

      The following trigger events are supported for use in Rules:

       ON MPU6050#Temperature DO <command> ENDON  
      + ON MPU6050#AccelXAxis DO <command> ENDON   
      + ON MPU6050#AccelYAxis DO <command> ENDON   
      + ON MPU6050#AccelZAxis DO <command> ENDON
      + ON MPU6050#GyroXAxis DO <command> ENDON    
      + ON MPU6050#GyroYAxis DO <command> ENDON    
      + ON MPU6050#GyroZAxis DO <command> ENDON
      +
      + ON MPU6050#Yaw DO <command> ENDON  
      + ON MPU6050#Roll DO <command> ENDON 
      + ON MPU6050#Pitch DO <command> ENDON
      +
      Example:
       ON MPU6050#Yaw DO publish espMotion/sensor/data/Yaw %value% ENDON
      +

      \ No newline at end of file diff --git a/MQTT/index.html b/MQTT/index.html new file mode 100644 index 0000000000..18c7baa1ea --- /dev/null +++ b/MQTT/index.html @@ -0,0 +1,27 @@ + MQTT - Tasmota
      Skip to content

      MQTT

      MQTT is the main protocol for controlling Tasmota devices

      After you have a working MQTT broker you need to configure Tasmota to communicate with it. If you have no knowledge of what MQTT is, you can learn about it from MQTT Essentials articles.

      Configure MQTT~

      If you flashed a precompiled .bin or didn't enter MQTT info in user_config_override.h before compiling you have to configure it on your device first.

      Configure MQTT using WebUI~

      Go to Configuration -> Configure Other and make sure "MQTT Enable" box is checked.
      Once MQTT is enabled you need to set it up using Configuration -> Configure MQTT.

      Tip

      While here, you might as well change the Friendly Name into something more descriptive than generic "Tasmota".
      This is highly recommended for Home Assistant autodiscovery feature.

      Enable MQTT Configure MQTT

      For a basic setup you only need to set Host, User and Password but it is recommended to change Topic to avoid issues. Each device should have a unique Topic.

      • Host = your MQTT broker address or IP (mDNS is not available in the official Tasmota builds, means no .local domain!)
      • Port = your MQTT broker port (default port is set to 1883)
      • Client = device's unique identifier. In 99% of cases it's okay to leave it as is, however some Cloud-based MQTT brokers require a ClientID connected to your account. Can not be identical to Topic!
      • User = username for authenticating on your MQTT broker
      • Password = password for authenticating on your MQTT broker
      • Topic = unique identifying topic for your device (e.g. hallswitch, kitchen-light). %topic% in wiki references to this. It is recommended to use a single word for the topic.
      • FullTopic = full topic definition. Modify it if you want to use multi-level topics for your devices, for example lights/%prefix%/%topic%/ or %prefix%/top_floor/bathroom/%topic%/ etc.

      Configure MQTT using Backlog~

      Using a serial connection or the WebUI Console you can issue (or even better, paste a premade) Backlog command for quick and easy MQTT setup.

      Backlog mqtthost <mqtt_broker_address>; mqttport <mqtt_broker_port>; mqttuser <username>; mqttpassword <password>; topic <device_topic>
      +

      After a reboot all necessary MQTT settings are configured. Don't forget, you can use Backlog for all commands!

      Commands over MQTT~

      To send commands and view responses you'll need an MQTT client.

      Commands over MQTT are issued to Tasmota by using topic cmnd/%topic%/<command> and payload <parameter>. If there is no <parameter> (an empty MQTT message/payload), a query is sent for current status of the <command>.

      Tip

      If you are using mosquitto_pub, you can issue an empty payload using the -n command line option. If your MQTT client cannot issue an empty payload, you can use the single character ? instead.

      Command flow~

      The following example will go in depth on what happens when you send an MQTT command.

      A device was flashed and configured with the FullTopic as default %prefix%/%topic%/ and the Topic set to tasmota_switch. We want to see the current status of the switch and change it.

      By looking at the commands table we can learn about the POWER command and options associated with it.

      Ask the device for status~

      cmnd/tasmota_switch/Power      // an empty message/payload sends a status query
      +     stat/tasmota_switch/RESULT  {"POWER":"OFF"}  
      +     stat/tasmota_switch/POWER  OFF
      +
      We can see that the switch (device's relay) is turned off.

      Send a command to toggle the relay~

      cmnd/tasmota_switch/Power TOGGLE
      +     // Power for relay 1 is toggled
      +     stat/tasmota_switch/RESULT  {"POWER":"ON"}  
      +     stat/tasmota_switch/POWER  ON
      +
      We've sent the toggle command and received confirmation that the switch is turned on.

      Tip

      By default, Tasmota replies to all commands through .../RESULT. This behavior can be changed using SetOption4, which makes the commands reply on the endpoint matching the command name, ex. cmnd/tasmota/PowerOnState will send a response on stat/tasmota/POWERONSTATE.

      Examples~

      In the following examples %topic% is tasmota, FullTopic is %prefix%/%topic%/, and prefixes are default cmnd/stat/tele:

      • The relay can be controlled with cmnd/tasmota/POWER on, cmnd/tasmota/POWER off or cmnd/tasmota/POWER toggle. Tasmota will send a MQTT status message like stat/tasmota/POWER ON.

      • Power state message can be sent with the retain flag set using PowerRetain 1.

      • Telemetry messages can also be sent with the retain flag using SensorRetain.

      • For Sonoff Dual or Sonoff 4CH the relays need to be addressed with cmnd/tasmota/POWER<x>, where {x} is the relay number from 1 to 2 (Sonoff Dual) or from 1 to 4 (Sonoff 4CH). cmnd/tasmota/POWER4 off turns off the 4th relay on a Sonoff 4CH.

      • MQTT topic can be changed with cmnd/tasmota/Topic tasmota1 which reboots Tasmota and changes the %topic% to tasmota1. From that point on MQTT commands should look like cmnd/tasmota1/POWER on.

      • The OTA firmware location can be made known to tasmota with cmnd/tasmota/OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin. Reset to default with cmnd/tasmota/OtaUrl 1.

      • Upgrade OTA firmware from the OtaUrl server with cmnd/tasmota/Upgrade 1.

      • Show all status information with cmnd/tasmota/Status 0.

      • The button can send a MQTT message to the broker that in turn will switch the relay. To configure this you need to perform cmnd/tasmota/ButtonTopic tasmota where tasmota equals to Topic. The message can also be provided with the retain flag by cmnd/tasmota/ButtonRetain on.

      • Sonoff Pow (and any device with sensors) status can be requested manually with cmnd/tasmota/status 8. Additionally, Tasmota periodically sends telemetry every TelePeriod, which defaults to 300 seconds (5 minutes).

      • When a Sonoff Pow (and any device with power metering sensors) threshold like PowerLow has been met a message tele/tasmota/POWER_LOW ON will be sent. When the error is corrected a message tele/tasmota/POWER_LOW OFF will be sent.

      While most MQTT commands will result in a message in JSON format the power status feedback will always be returned like stat/tasmota/POWER ON as well.

      Telemetry data will be sent by prefix tele like tele/tasmota/SENSOR {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}

      MQTT Topic Definition~

      FullTopic~

      This is the MQTT topic used to communicate with Tasmota over MQTT. It is created using tokens placed within a user definable string (100 character limit). The tokens are substituted dynamically at run-time. Available substitution tokens are:

      • %prefix% = one of three prefixes as defined by commands Prefix1 (default = cmnd), Prefix2 (default = stat) and Prefix3 (default = tele).
      • %topic% = one of five topics as defined by commands Topic, GroupTopic, ButtonTopic, SwitchTopic and MqttClient.
      • %hostname% = the hostname of the device as defined through the web UI (default = %s-%04d) or via Hostname command.
      • %id% = MAC address of the device.

      Tip

      These substitution tokens will be used in examples across the documentation

      If FullTopic does not contain the %topic% token, the device will not subscribe to GroupTopic and FallbackTopic.

      Tip

      The order of %prefix% and %topic% doesn't matter, unless you have enabled Auto-discovery for Home Assistant (SetOption19). Enabling this option re-formats the FullTopic to required order.

      Using the tokens the following example topics can be made:

      • FullTopic %prefix%/%topic%/ default
      • FullTopic tasmota/%topic%/%prefix%/
      • FullTopic tasmota/bedroom/%topic%/%prefix%/
      • FullTopic penthouse/bedroom1/bathroom2/%topic%/%prefix%/
      • FullTopic %prefix%/home/cellar/%topic%/

      %prefix%~

      Tasmota uses 3 prefixes for forming a FullTopic:

      • cmnd - prefix to issue commands; ask for status
      • stat - reports back status or configuration message
      • tele - reports telemetry info at specified intervals

      Warning

      To solve possible MQTT topic loops it is strongly suggested that you use the %prefix% token in all of your FullTopics. Status and telemetry do not need a prefix.

      %topic%~

      All MQTT status messages will be sent using the configurable %topic% which must be made unique by the user. It can be called bedroom but it could also be called XP-TS_10 as long as the user knows what it is and where to find it.

      Default Topic is tasmota_XXXXXX where XXXXXX is unique and derives from the last 6 characters of the MAC address

      The use of the %topic% token is mandatory in case you want to use ButtonTopic and/or SwitchTopic. It also provides for GroupTopic and Fallback Topic functionality.

      Tip

      Use %prefix% and %topic% tokens at all time within your FullTopic definition!

      GroupTopic~

      Having two devices with the same topic allowed for MQTT commands to be sent once to make the devices act in conjunction. That inspired a third topic called GroupTopic. Devices with the same GroupTopic will all listen to that GroupTopic and react to the same MQTT command sent to it. You can use this to take global actions like updating firmware on all devices or split up devices into different groups using a unique GroupTopic for each group.

      Default GroupTopic is tasmotas

      FallBack Topic~

      Initially Tasmota had one MQTT configurable topic planned called Topic. It soon became apparent that when two devices come online with the same topic this would be a challenge, to say the least!

      Tasmota then introduced a unique, non-configurable "FallBack Topic" that allows MQTT communication regardless of the configurable topic. This fallback topic is just what it is meant to be: a fallback topic in case of emergency!

      By default the Fallback Topic is DVES_XXXXXX_fb where xxxxxx is derived from the last 6 characters of the device's MAC address (excluding :). It might look something like this: DVES_3D5E26_fb. You can find out the DVES code by looking at Information page in the webUI or issuing Status 6:

      12:36:17 MQT: stat/tasmota/STATUS6 = {"StatusMQT":{"MqttHost":"1.1.1.1","MqttPort":1883,"MqttClientMask":"DVES_%06X","MqttClient":"DVES_3D5E26","MqttUser":"tasmota","MqttCount":1,"MAX_PACKET_SIZE":1000,"KEEPALIVE":30}}
      +

      LWT Topic (Last Will and Testament)~

      On connection to the MQTT broker tasmota uses the Last Will and Testament (LWT) feature that instructs the broker to generate a notification about an ungracefully disconnect or timeout.

      The topic to listen to for the state of this specific client is tele/<TOPIC>/LWT that with the mosquitto MQTT broker produces an output like:

      $ mosquitto_sub -t "tele/tasmota_XXXXXX/LWT"
      +Offline
      +Online
      +

      The full LWT topic can be found in the tasmota console at boot:

      15:51:51.281 MQT: tele/tasmota_XXXXXX/LWT = Online (retained)
      +

      Retained MQTT Messages~

      If MQTT is defined and retain option is used the last state will be stored permanently in MQTT database.

      Command Description
      InfoRetain Show current MQTT info retain state.
      0 / off = disable MQTT info retain (default)
      1 / on = enable MQTT info retain
      PowerRetain Show current MQTT power retain state.
      0 / off = disable MQTT power retain on status update (default)
      1 / on = enable MQTT power retain on status update
      SensorRetain Show current MQTT sensor retain state.
      0 / off = disable MQTT sensor retain on sensor update (default)
      1 / on = enable MQTT sensor retain on sensor update
      StateRetain Show current MQTT state retain state.
      0 / off = disable MQTT state retain (default)
      1 / on = enable MQTT state retain
      StatusRetain Show current MQTT status retain state.
      0 / off = disable MQTT status retain (default)
      1 / on = enable MQTT status retain
      SwitchRetain Show current MQTT switch retain state.
      0 / off = disable MQTT switch retain on switch update (default)
      1 / on = enable MQTT switch retain on switch update

      BUT, a power message in your MQTT broker flagged as 'retained' will always override the PowerOnState.

      This is usually the main cause for "ghost switching". Learn more in MQTT retained messages explained. Check out this tutorial for troubleshooting switch ghosting.

      Clearing Retained Messages~

      To check, if there is a retain flag set for the switch's power topic, monitor cmnd/+/power in a MQTT client (recommended MQTT.fx for Windows or Eclipse Mosquitto in linux).

      You can use Tasmota Device Manager to clear all retained messages for selected device with a single click.

      Tasmota Device Manager

      or use the following tutorials/forum threads:

      Subscribe/Unsubscribe~

      This feature is not included in precompiled binaries

      To use it you must compile your build. Add the following to user_config_override.h:

      #ifndef SUPPORT_MQTT_EVENT
      +#define SUPPORT_MQTT_EVENT
      +#endif
      +

      Subscribe~

      Subscribes to an MQTT topic and assigns an Event name to it.

      Subscribe <eventName>, <mqttTopic> [, <key>]

      The <key> parameter is specified when you need to parse a key/value pair from a JSON payload in the MQTT message. In order to parse a value from a multi-level JSON pair, you can use one dot (.) syntax to split the key into sections.

      You subscribe to an MQTT topic and assign an event name. Once the subscribed MQTT message is received the configured event will be triggered.

      Command without any parameters will list all currently subscribed topics.

      You can set up a rule with ON EVENT#<event_name> DO ... ENDON to do what you want based on this MQTT message. The payload is passed as a parameter once the event has been triggered. If the payload is in JSON format, you are able to get the value of specified key as a parameter.

      For example, if you have a Tasmota based thermostat and multiple temperature sensors in different locations, usually you have to set up a home automation system like Domoticz to control the thermostat. Right now, with this new feature, you can write a rule to do this.

      Examples~

      Rule1
      +  ON mqtt#connected DO Subscribe BkLight, stat/other-device-topic/POWER ENDON
      +  ON Event#BkLight=ON DO <command> ENDON
      +
      Rule1
      +  ON mqtt#connected DO Subscribe DnTemp, stat/other-device-topic/SENSOR, DS18B20.Temperature ENDON
      +  ON Event#DnTemp>=21 DO <command> ENDON
      +
      where the MQTT message payload is {"Time":"2017-02-16T10:13:52", "DS18B20":{"Temperature":20.6}}
      Rule1
      +  ON mqtt#connected DO Subscribe DnTemp, stat/other-device-topic/SENSOR, DS18B20.Temperature ENDON
      +  ON Event#DnTemp DO TempMeasuredSet %value% ENDON
      +
      will allow a thermostat to subscribe to a temperature sensor on another Tasmota device
        ON Event#DnTemp DO TempMeasuredSet %value% ENDON
      +
      Will allow a thermostat to subscribe to a temperature sensor on another Tasmota device

      Unsubscribe~

      Unsubscribe from topics which were subscribed to using the Subscribe command.

      Unsubscribe from a specific MQTT topic
      Unsubscribe <eventName>

      The command without a parameter will unsubscribe from all currently subscribed topics.

      Return codes (rc)~

      Sometimes, something wrong can happen and you might need to check return codes.

      A return code can be found in the console, example output for Return Code = 5 : MQT: Connect failed to xxxx:1883, rc 5. Retry in 10 sec

      Below table provides more information about it. The original values are related to PubSubClient.h constants.

      Code Constant name Description
      -5 MQTT_DNS_DISCONNECTED the DNS server cannot be reached or name cannot be resolved
      -4 MQTT_CONNECTION_TIMEOUT the server didn't respond within the keepalive time
      -3 MQTT_CONNECTION_LOST the network connection was broken
      -2 MQTT_CONNECT_FAILED the network connection failed
      -1 MQTT_DISCONNECTED the client is disconnected cleanly
      0 MQTT_CONNECTED the client is connected
      1 MQTT_CONNECT_BAD_PROTOCOL the server doesn't support the requested version of MQTT
      2 MQTT_CONNECT_BAD_CLIENT_ID the server rejected the client identifier
      3 MQTT_CONNECT_UNAVAILABLE the server was unable to accept the connection
      4 MQTT_CONNECT_BAD_CREDENTIALS the username/password were rejected
      5 MQTT_CONNECT_UNAUTHORIZED the client was not authorized to connect
      \ No newline at end of file diff --git a/MacOSX-Server/index.html b/MacOSX-Server/index.html new file mode 100644 index 0000000000..85fa68978a --- /dev/null +++ b/MacOSX-Server/index.html @@ -0,0 +1,5 @@ + MacOSX Server - Tasmota

      MacOSX Server

      Mac OSX comes with a built in web server, although it's not running by default. Starting the web server can be done by running:

      sudo apachectl start
      +

      To start the server, you'll be asked for your user's password.

      Opening your localhost will show you 'it works!', which will tell you that much - it works.

      The folder to put files in is

      /Library/WebServer/Documents
      +

      You may wish to create a subfolder for the firmware files.

      Use the IP address of this local web server to OTA flash Tasmota. You can determine your IP address here. For example,

      http://192.168.1.123/tasmota.bin
      +

      Issue this command to stop the web server when your are done flashing:

      sudo apachectl stop
      +

      Note: The MAMP web server does not seem to deliver the results as expected!

      \ No newline at end of file diff --git a/Matter-Internals/index.html b/Matter-Internals/index.html new file mode 100644 index 0000000000..47d087eb5e --- /dev/null +++ b/Matter-Internals/index.html @@ -0,0 +1,30 @@ + Matter Internals - Tasmota
      Skip to content

      Matter Internals~

      Matter protocol is experimental

      Matter is only supported on ESP32x based devices and requires a specific build with #define USE_MATTER_DEVICE

      Below are implementation notes to understand and extend Matter.

      Plugin System~

      The plugin system is designed to have different implementations for different types of devices or sensors. Each Matter endpoint is managed by an instance of a Plugin class.

      Endpoint root (0) is managed by the matter.Plugin_Root class because of its specific behavior.

      We provide currently the following classes:

      Plug-in class Description
      Plugin_Device Generic device (abstract)
      Plugin_Root. Root node (type 0x0016)
      Plugin_Aggregator Aggregator for Bridge mode (type 0x000E)
      Plugin_OnOff Simple On/Off Plug (type 0x010A)
      Plugin_Light0 Light with 0 channel (OnOff) (type 0x0100)
      Plugin_Light1 Light with 1 channels (Dimmer) (type 0x0101)
      Plugin_Light2 Light with 2 channels (CT) (type 0x010C)
      Plugin_Light3 Light with 3 channels (RGB) (type 0x010D)
      Plugin_Sensor Generic Sensor class (abstract)
      Plugin_Sensor_Temp Temperature Sensor (type 0x0302)
      Plugin_Sensor_Pressure Pressure Sensor (type 0x0305)
      Plugin_Sensor_Light Light/Illuminance Sensor (type 0x0106)
      Plugin_Sensor_Humidity Humidity Sensor (type 0x0307)
      Plugin_Sensor_Occupancy Occupancy Sensor linked to a swithc (type 0x0107)

      Tasmota is also able to act as a Bridge to other Tasmota devices (ESP8266 or ESP32) and drive them via the HTTP API. The following classes provide such features:

      Plug-in class Description
      Plugin_Bridge_HTTP Generic superclass for remote devices (abstract)
      Plugin_Bridge_OnOff Simple On/Off Plug (type 0x010A)
      Plugin_Bridge_Light0 Light with 0 channel (OnOff) (type 0x0100)
      Plugin_Bridge_Light1 Light with 1 channels (Dimmer) (type 0x0101)
      Plugin_Bridge_Light2 Light with 2 channels (CT) (type 0x010C)
      Plugin_Bridge_Light3 Light with 3 channels (RGB) (type 0x010D)
      Plugin_Bridge_Sensor Generic Sensor class (abstract)
      Plugin_Bridge_Sensor_Temp Temperature Sensor (type 0x0302)
      Plugin_Bridge_Sensor_Pressure Pressure Sensor (type 0x0305)
      Plugin_Bridge_Sensor_Light Light/Illuminance Sensor (type 0x0106)
      Plugin_Bridge_Sensor_Humidity Humidity Sensor (type 0x0307)
      Plugin_Bridge_Sensor_Occupancy Occupancy Sensor linked to a swithc (type 0x0107)

      Plugins Hierarchy:

      Matter_Plugin
      ++--- Matter_Plugin_Root
      ++--- Matter_Plugin_Aggregator
      ++--+ Matter_Plugin_Device
      +   +--+ Matter_Plugin_Light0
      +   |  |--+ Matter_Plugin_Light1
      +   |     |--- Matter_Plugin_Light2
      +   |     |--- Matter_Plugin_Light3
      +   +--- Matter_Plugin_OnOff
      +   +--+ Matter_Plugin_Shutter
      +   |  +--- Matter_Plugin_ShutterTilt
      +   +--+ Matter_Plugin_Sensor
      +   |  +--- Matter_Plugin_Sensor_Humidity
      +   |  +--- Matter_Plugin_Sensor_Temperature
      +   |  +--- Matter_Plugin_Sensor_Pressure
      +   |  +--- Matter_Plugin_Sensor_Illuminance
      +   |  +--- Matter_Plugin_Sensor_Occupancy
      +   +--+ Matter_Plugin_Bridge_HTTP
      +      +--+ Matter_Plugin_Bridge_Light0
      +      |  +--+ Matter_Plugin_Bridge_Light1
      +      |  |  +--- Matter_Plugin_Bridge_Light2
      +      |  |  +--- Matter_Plugin_Bridge_Light3
      +      |  +--- Matter_Plugin_Bridge_OnOff
      +      +--+ Matter_Plugin_Bridge_Sensor
      +      |  +--- Matter_Plugin_Bridge_Sensor_Humidity
      +      |  +--- Matter_Plugin_Bridge_Sensor_Temperature
      +      |  +--- Matter_Plugin_Bridge_Sensor_Pressure
      +      |  +--- Matter_Plugin_Bridge_Sensor_Illuminance
      +      +--- Matter_Plugin_Bridge_Sensor_Occupancy
      +

      Plugin superclass~

      All plugins inherit from the Matter_Plugin superclass.

      Note: for solidification to succeed, you need to declare class Matter_Plugin end fake class in the same Berry file. The actual class will be used in solidified code.

      Plugin method Description
      init(device, endpoint) (can be overridden) Instantiate the plugin on a specific endpoint. You need to pass the root matter_device object

      Core classes~

      class matter.Device used as monad matter_device~

      matter_device is a monad of matter.Device automatically created at boot. It checks if Matter si enabled (SetOption151 1) and instantiates all sub-systems.

      Device attributes~

      Device variables Description
      plugins List of matter.Plugin().
      Each plugin manages a distinct endpoint and the associated sub-device behavior
      udp_server instance of matter.UDPServer() and is used to (re-)send and receive UDP packets
      message_handler instance of matter.MessageHandler(), handles the dispatching of incoming packets to the relevant layers.
      sessions instance of matter.Session_Store() which holds a list of matter.Session()
      All active persistent and non-persistent sessions are listed here, and serve to dispatch incoming packets
      Session are also linked to Fabric when persisted
      ui instance of matter.UI()
      Handles the web UI for Matter.

      The following are saved as Matter device configuration

      Configuration variables Description
      root_discriminator as int
      root_passcode as int
      ipv4_only (bool) advertize only IPv4 addresses (no IPv6)
      nextep (int) next endpoint to be allocated for bridge, start at 51

      When commissioning is open, here are the variables used:

      Commissioning variables Description
      commissioning_open timestamp for timeout of commissioning (millis()) or nil if closed
      commissioning_iterations current PBKDF number of iterations
      commissioning_discriminator commissioning_discriminator
      commissioning_salt current salt
      commissioning_w0 current w0 (SPAKE2+)
      commissioning_L current L (SPAKE2+)
      commissioning_instance_wifi random instance name for commissioning (mDNS)
      commissioning_instance_eth random instance name for commissioning (mDNS)

      For default commissioning, the following values are used (and can be changed via UI):

      Root Commissioning variables Description
      root_iterations PBKDF number of iterations
        PBKDF information used only during PASE (freed afterwards)
      root_salt
      root_w0
      root_L

      Device methods~

      Method Description
      start_root_basic_commissioning(timeout_s) Start Basic Commissioning with root/UI parameters
      Open window for timeout_s (default 10 minutes)
      remove_fabric(fabric) Remove a fabric and clean all corresponding values and mDNS entries
      start_basic_commissioning(timeout_s, iterations, discriminator, salt, w0, L, admin_fabric) Start Basic Commissioning Window with custom parameters
      is_root_commissioning_open() Is root commissioning currently open. Mostly for UI to know if QRCode needs to be shown.
      stop_basic_commissioning() Stop PASE commissioning, mostly called when CASE is about to start
      compute_qrcode_content() Compute QR Code content - can be done only for root PASE
      compute_manual_pairing_code() Compute the 11 digits manual pairing code (without vendorid nor productid) p.223
      can be done only for root PASE (we need the passcode, but we don't get it with OpenCommissioningWindow command)
      every_second() Dispatch second-resolution ticks to: sessions, message_handler, plugins.
      Expire commissioning window.
      Called by Tasmota loop.
      start_operational_discovery_deferred(session) Start Operational Discovery for this session
      Deferred until next tick.
      start_commissioning_complete_deferred(session) Start Commissioning Complete for this session
      Deferred until next tick.
      start_operational_discovery(session) Start Operational Discovery for this session
      Stop Basic Commissioning and clean PASE specific values (to save memory). Announce fabric entry in mDNS.
      start_commissioning_complete(session) Commissioning Complete
      Stop basic commissioning.
      get_active_endpoints(exclude_zero) Return the list of endpoints from all plugins (distinct), exclude endpoint zero if exclude_zero is true
      save_param() Persistence of Matter Device parameters
      load_param() Load Matter Device parameters

      Incoming messages handing

      Method Description
      msg_received(raw, addr, port) Callback when message is received.
      Send to message_handler
      msg_send(raw, addr, port, id) Global entry point for sending a message.
      Delegates to udp_server
      received_ack(id) Signals that a ack was received.
      Delegates to udp_server to remove from resending list.
      attribute_updated(endpoint, cluster, attribute, fabric_specific) Signal that an attribute has been changed and propagate to any active subscription.
      Delegates to message_handler
      process_attribute_expansion(ctx, cb) Proceed to attribute expansion (used for Attribute Read/Write/Subscribe)
      Called only when expansion is needed, so we don't need to report any error since they are ignored

      Calls cb(pi, ctx, direct) for each attribute expanded.
      pi: plugin instance targeted by the attribute (via endpoint). Note: nothing is sent if the attribute is not declared in supported attributes in plugin.
      ctx: context object with endpoint, cluster, attribute (no command)
      direct: true if the attribute is directly targeted, false if listed as part of a wildcard
      returns: true if processed successfully, false if error occurred. If direct, the error is returned to caller, but if expanded the error is silently ignored and the attribute skipped.
      In case of direct but the endpoint/cluster/attribute is not supported, it calls cb(nil, ctx, true) so you have a chance to encode the exact error (UNSUPPORTED_ENDPOINT / UNSUPPORTED_CLUSTER / UNSUPPORTED_ATTRIBUTE / UNREPORTABLE_ATTRIBUTE)
      invoke_request(session, val, ctx) Matter plugin management
      Plugins allow to specify response to read/write attributes and command invokes

      UDPPacket_sent~

      Method Description
      init(raw, addr, port, id) Create raw UDP packet with bytes() content, target addr (string) and port (int). If id is not nil enqueue until acked
      send(socket) Send packet now. Returns true if packet was successfully sent.

      mDNS (DNS-SD)~

      Method Description
      start_mdns_announce_hostnames() Start mDNS and announce hostnames for Wi-Fi and ETH from MAC
      When the announce is active, hostname_wifi and hostname_eth are defined
      mdns_announce_PASE() Announce MDNS for PASE commissioning
      mdns_remove_PASE() MDNS remove any PASE announce
      mdns_announce_op_discovery_all_fabrics() Start UDP mDNS announcements for commissioning for all persisted sessions
      mdns_announce_op_discovery(fabric) Start UDP mDNS announcements for commissioning
      mdns_remove_op_discovery_all_fabrics() Remove all mDNS announces for all fabrics
      mdns_remove_op_discovery(fabric) Remove mDNS announce for fabric
      save_before_restart() Try to clean MDNS entries before restart.
      Called by Tasmota loop as a Tasmota driver.

      Class UDPServer~

      This class creates a monad (singleton) in charge of receiving and sending all UDP packets. Packets to sent are generally put in a queue, and are re-sent with exponential backoff until they are acknowledged by the receiver (as part of Matter over UDP) or after the maximum of retries have been made.

      Method Description
      init(addr, port) Init UDP Server listening to addr and port (opt). By default, the server listens to "" (all addresses) and port 5540
      start(cb) Starts the server. Registers as device handler to Tasmota.
      cb(packet, from_addr, from_port): callback to call when a message is received.
      Raises an exception if something is wrong.
      stop() Stops the server and remove driver
      every_50ms() At every tick: Check if a packet has arrived, and dispatch to cb. Read at most `MAX_PACKETS_READ (4) packets at each tick to avoid any starvation.
      Then resend queued outgoing packets.
      _resend_packets() Resend packets if they have not been acknowledged by receiver either with direct Ack packet or ack embedded in another packet. Packets with id=nil are not resent.
      Packets are re-sent at most RETRIES (4) times, i.e. sent maximum 5 times. Exponential back off is added after each resending.
      If all retries expired, remove packet and log.
      received_ack() Just received acknowledgment, remove packet from sender
      send_response(raw, addr, port, id, session_id) Send a packet, enqueue it if id is not nil.
      session_id is only used for logging.

      MessageHandler~

      matter_device.message_handler is a monad of matter.MessageHandler

      Dispatches incoming messages and sends outgoing messages

      Variables of Message Handler Description
      device Reference to the global matter_device instance
      commissioning Commissioning Context instance, handling the PASE/CASE phases
      im Instance of matter.IM handling Interaction Model

      General methods:

      Method Description
      init(device) Constructor, instantiates monads for commissioning and im
      msg_received(raw, addr, port) Called by matter_device when a message is received.
      - decodes the message header
      - associates the message with the corresponding active session, or create a new session
      - dispatches to commissioning or im depending on the message type
      - sends an Ack packet if the received packet had the reliable flag set and if the Ack was not already managed upper stack.
      send_response(raw, addr, port, id, session_id) Send a packet. Proxy to the same method in device

      TLV~

      Implements the TLV encoding and decoding as defined in Appendix A of the Matter specification. TLV stands for Tag-length-value encoding. It is a way to encode tagged values and structures in a binary compact format. Most Matter messages are encoded in TLV.

      Parse and print: m = matter.TLV.parse(b) print(m)

      TLV Types~

      Type Description
      I1 I2 I4 Signed integer of at most (1/2/4) bytes (as 32 bits signed Berry type)
      U1 U2 U4 Unsigned integer of at most (1/2/4) bytes (as 32 bits signed Berry type, be careful when comparing. Use matter.Counter.is_greater(a,b))
      I8 U8 Signed/unsigned 8 bytes. You can pass bytes(8), int64() or int. Type is collapsed to a lower type if possible when encoding.
      BOOL Boolean, takes true and false. Abstracts the internal BTRUE and BFALSE that you don't need to use
      FLOAT 32 bites float
      UTF1 UTF2 String as UTF, size is encoded as 1 or 2 bytes automatically
      B1 B2 raw bytes(), size is encoded as 1 or 2 bytes automatically
      NULL takes only nil value
      STRUCT
      ARRAY
      LIST
      EOC
      (internal) Use through abstractions
      DOUBLE
      UTF4 UTF8
      B4 B8
      Unsupported in Tasmota

      Creating TLV~

      Simple value: matter.TLV.create_TLV(type, value)

      Example: matter.TLV.create_TLV(matter.TLV.UTF1, "Hello world") matter.TLV.create_TLV(matter.TLV.BOOL, true) matter.TLV.create_TLV(matter.TLV.NULL, nil) matter.TLV.create_TLV(matter.TLV.FLOAT, 3.5) matter.TLV.create_TLV(matter.TLV.I2, -345) matter.TLV.create_TLV(matter.TLV.U8, bytes("DEADBEEFDEADBEEF"))

      Subscriptions~

      When a subscription is issued by an initiator, we create an instance of matter.IM_Subscription which holds:

      • the CASE session on which the subscription was issued. If the session is closed, the subscription dies. Subscriptions are not persisted and stop if reboot
      • subscription_id (int) used to tell the initiator which subscription it was
      • path_list list of matter.Path instances recording all the attributes subscribed to. They can include wildcards
      • min_interval and max_interval (in seconds): Tasmota waits at least min_interval before sending a new value, and sends a message before max_interval (usually heartbeats to signal that the subscription is still alive). Generally changes to attributes are dispatched immediately.
      • fabric_filtered: not used for now

      Below are internal arguments:

      • not_before: the actual timestamp that we should wait before sending updates, as to respect min_interval
      • expiration: the maximum timestamp we can wait before sending a heartbeat. Both are updated after we sent a new value
      • wait_status: signals that we sent everything and we wait for the final StatusReport to resume sending further updates
      • is_keep_alive (bool) did the last message was a keep-alive, if so we just expect a Ack and no StatusReport
      • updates: list of concrete attributes that have values changed since last update. They don't contain the new value, we will actually probe each attribute value when sending the update

      IM_Subscription_Shop~

      This class (monad) contains the global list of all active subscriptions. Method|Description :----|:--- init(im)|Instantiate the monad with the global IM monad new_subscription(session, req)|Take a session and a SubscribeRequestMessage, parse the message and create a matter.IM_Subscription instance.
      Returns the matter.IM_Subscription() instance. Also allocates a new subscription id.

      What happens when an attribute is updated~

      Subscriptions are triggered by the value of an attribute changing. They can originate from an explicit WRITE Matter command from an initiator or another device, of be the consequence of a Matter command (like switching a light ON). The can also originated from independent source, like an action at Tasmota level (using Tasmota Power command), or Tasmota detecting that a sensor value has changed after periodical polling.

      Note: default plugins for Lights actually probe Tasmota light status every second, and report any difference between the last known change (also called shadow value) and the current status. We realized that it was more consistent and reliable than trying to create rules for every event.

      When an attribute's value changed, you need to call the plugin's method
      self.attribute_updated(<endpoint_id>, <cluster_id>, <attribute_id> [, <fabric_specific>])

      <fabric_specific> (bool) is optional and currently ignored and reserved for future use.

      The endpoint_id argument is optional. If the endpoint is unknown, the message is broadcast to all endpoints that support reading this attributes:
      self.attribute_updated(nil, <cluster_id>, <attribute_id>)

      More generally, you can use the global method to signal an attribute change from any code:
      matter_device.attribute_updated(nil, <cluster_id>, <attribute_id>)

      Note: internally this method creates a matter.Path instance and calls
      matter_device.message_handler.im.subs_shop.attribute_updated_ctx(ctx, fabric_specific)
      which in turns calls attribute_updated_ctx(ctx, fabric_specific) on every active subscription.

      Calls to attribute_updated_ctx() are first check whether the attribute matches the filtering criteria's (that may include wildcards). If they match, the attribute is candidate to be added to the list. We then call _add_attribute_unique_path() to check if the attribute is not already in the list, and if not add it to the list of future updates. It's possible that during the min_interval time, an attribute may change value multiple times; yet we publish a single value (the last one).

      Updates~

      TheSubscription_Shop monad checks every 250ms if there are updates ready to be sent via every_250ms().

      It does a first scan across all active subscriptions if updates can be sent out:

      • subscription is not in wait_status (i.e. not waiting for a previous exchange to complete)
      • subscription has a non-empty list of updates
      • subscription has reached the not_before timestamp (so as to not sent too frequent updates)

      If so:

      • im.send_subscribe_update(sub) is called
      • the subscription list of updates is cleared via sub.clear_before_arm()

      Once all updates are sent, the subscription are scanned again to see if any heartbeat needs to be sent:

      • subscription is not in wait_status
      • subscription has reached expiration timestamp If so:
      • im.send_subscribe_update(sub) is called
      • the subcription list of updates is cleared via sub.clear_before_arm() XXX TODO

      Extending Matter~

      All Matter support code is located in berry_matter as a lib, which avoids polluting the main directory of drivers. Berry allows to develop much faster compared to C++, and performance is mostly not an issue with Matter.

      The Berry code is located in the embedded directory. Then the code is compiled into bytecode and the bytecode is stored in Flash. This avoids consuming RAM which is a very previous resource on ESP32. To solidify, you just need to run ./solidify_all.be in berry_matter. But before you need to have a local version of Berry: in berry directory, just do make. For windows users, compiling Berry can be challenging so a pre-compiled berry.exe is provided.

      \ No newline at end of file diff --git a/Matter-with-Google/index.html b/Matter-with-Google/index.html new file mode 100644 index 0000000000..7532886598 --- /dev/null +++ b/Matter-with-Google/index.html @@ -0,0 +1 @@ + Beginners Guide to adding Tasmota Matter to Google Home - Tasmota
      Skip to content

      Beginners Guide to adding Tasmota Matter to Google Home~

      Google Home will not accept devices using a test Vendor ID like Tasmota does. To pair Tasmota with Google Home you will need a Matter compatible Google device and the following procedure to enable Tasmota for your Google Home account.

      Create a Project~

      Open Google Home Developer Console logged in under the same account used for Google Home.

      Click on Create a project then Create project. Enter a name for your project (f.e. "Matter Tasmota").

      Step 1

      Add Matter Integration~

      Click on "Add Matter Integration"

      Step 2

      Click Next: Develop then Next:Setup

      Enter Device Information~

      Enter the name for the devices used.

      Step 3

      Vendor ID needs to be 0xFFF1 as selected by default.

      Product ID used by Tasmota devices is 0x8000.

      Once you've filled out the required fields click on Save & continue

      Configure setup and branding (optional)~

      At this step you can change the images used in Google Home app during device commissioning. There is no need to fill out Android package name field.

      Step 4

      Click Save

      Setup Complete~

      Main page will now have an integration as shown:

      Step 5

      Add Device to Google Home~

      Activate Open Commissioning on the Tasmota device and then click on add new device, then select New device in the Google Home app. Tasmota device should be discovered as a Matter device. Scan the QR code from the web UI and click on Agree to connect the Tasmota device to Google Home.

      Step 7

      \ No newline at end of file diff --git a/Matter/index.html b/Matter/index.html new file mode 100644 index 0000000000..e2e5564880 --- /dev/null +++ b/Matter/index.html @@ -0,0 +1,3 @@ + Matter - Tasmota
      Skip to content

      Matter~

      Matter protocol supported in all ESP32 variants (C3/S2/S3) since Tasmota v13.0.0. ESP8266 is not supported although ESP8266 devices can be handled via a single ESP32 in bridge mode (see below)

      This feature is included in standard tasmota32xx builds; not in special variants (display, sensors...)

      When compiling your build add the following flag to the build environment or user_config_override.h:

      #define USE_MATTER_DEVICE
      +

      What is Matter?~

      The Matter protocol is an open-source, royalty-free standard designed to enable smart home devices from different manufacturers to work seamlessly and securely together. The Matter standard was launched end of 2022, and is supported since July 2023 by Tasmota v13.0 and above. Learn more about Matter.

      Tasmota supports Matter over IP (Wi-Fi or Ethernet) as an end-device. This means that you can connect a Tasmota device to a Matter gateway, using standard and well defined protocols. The Matter communication is completely local.

      Matter cannot be directly supported on ESP8266 due to limited memory resources but you can use an ESP32 device as a Matter bridge to other Tasmota and OpenBK devices.

      Tasmota-Matter

      Thread is not supported

      Thread requires a specific radio and is not supported by ESP32 devices, it would require an additional, separate MCU. Espressif has launched a certified Thread Border Router based on ESP32S3 + ESP32H2 which should allow to bridge Thread device to a Wifi/Ethernet local network. Stay tuned...

      Supported platforms~

      Tasmota Matter is confirmed to work with:

      More information on pairing with above mentioned smart home systems.

      Getting Started~

      Matter is supported by default. The binaries are available for all ESP32 variants in our web installer

      Go to Configuration --> Configure Matter and enable Matter with the checkmark then click Save.

      Enable Matter

      After a restart device commissioning will be open for 10 minutes.

      Device commissioning

      Add the device to your Matter hub by scanning the QR code or with the "Manual pairing code" if code scanning is not possible.

      Configuration~

      After Matter support is enabled the Configuration --> Configure Matter menu is used to configure options.

      Matter configuration

      Here you can enable/disable Matter or open commissioning again if needed.

      Current Configuration~

      Configuration of your device for Matter discovery. Tasmota tries to configure supported features automatically.

      You can change the name of the Matter endpoint, switch its Parameter number or remove the endpoint by clicking the 🔥 icon.

      Matter configuration

      After changing these options click Change configuration.

      Add to configuration~

      Add local sensor or device~

      To add new features you need to add Endpoints.

      Add to configuration

      Name~

      Name your endpoint to give it a label in your Matter hub (might not be supported by all hubs)

      Type~

      Select your endpoint type from the drop down. Types are limited by the Matter specification and some types might not be supported by the Matter hub.

      Parameter~

      To link a Tasmota power output (light/relay) to an endpoint set its parameter equal to the POWER number.

      A relay on POWER2 will have Parameter set to 2

      To link a Tasmota sensor to an endpoint add the rule trigger of the sensor to the Parameter value.

      You can easily find out the parameter value by issuing Status 8 in the console:

      MQT: tele/tasmota/SENSOR = {"Time":"2023-05-28T20:04:27","SCD40":{"CarbonDioxide":618,"eCO2":623,"Temperature":23.8,"Humidity":61.1,"DewPoint":15.8},"TempUnit":"C"}
      +

      Parameter for a Temperature endpoint will be SCD#Temperature and the Parameter for a Humidity endpoint will be SCD#Humidity in this case.

      Endpoint configuration

      After entering click Create new endpoint and it will appear in the Current configuration list

      Endpoint configured

      Add Remote Tasmota or OpenBK~

      With this option you can bridge any existing Tasmota or OpenBK device to Matter.

      Add Remote Tasmota or OpenBK

      Enter the IP or hostname of the remote device and click Auto-configure remote Tasmota

      This will bring you to another menu where you can further configure the remote device.

      Add Remote Tasmota or OpenBK submenu

      Add remote Endpoints same as for a local sensors and devices.

      When finished click Add endpoints. The remote device will appear in the Current configuration list.

      Add Remote Tasmota or OpenBK configured

      Note

      Full Bridge mode is not yet supported, which means that currently you can't dynamically add new devices/endpoints. Adding a new Tasmota-end-device requires to remove the border router from the controller and pair it again. This will be addressed in the near future.

      When a command arrives from the Matter controller, it is passed immediately to the end-device. Any change made on the end-device is eventually sent back to Matter controller via polling. Polling is done every 3 seconds for lights and relays and every 5 seconds for sensors.

      The Matter border router needs to have IP connectivity to the Tasmota end-device to send HTTP requests. However they don't need to be on the same VLAN (contrary to Matter which needs the controller and the device to be on the same VLAN).

      There is a limit to the number of endpoints

      Matter bridge cannot support a large number of endpoints since the numerous HTTP requests will impact performance. It is recommended to limit the number of remote endpoints to 8 per Matter bridge.

      Reset all and Auto-discover~

      This option will reset all configured endpoints and try to auto discover them again.

      Advanced Configuration~

      Matter Passcode~

      You can change the passcode and ID of the device or force it to use IPv4 only.

      Matter Passcode

      Fabrics~

      In short fabrics are Matter node identifiers. Long story here....

      Fabrics

      Deleting a Fabric will remove its commissioned status from the Matter hub.

      Known Limitations~

      Matter support is still in testing, there are some limitations:

      • Matter requires IPv6 support, yet IPv4 is often tolerated. For debug purpose you can force Tasmota to manage Matter only on IPv4
      • Tasmota devices with dual active networks (Ethernet + Wifi) are not yet supported. You need to disable Wifi or Ethernet. For example use Ethernet Network Flipper
      • Tasmota cannot be Matter certified, it uses development vendor id's, which typically raise user warnings when commissioning the device
      • Matter is also in an early phase with hub development and not all hubs support all Matter device types yet

      What's not suported:

      • Thread as it requires a separate MCU. The number of Thread devices is still very limited.
      • ESP8266 directly because of limited memory resources and lack of Berry support but you can add them to Matter via the Remote option
      • Zigbee

      Commands~

      Command Description
      MtrJoin 1 = open commissioning for 10 minutes
      0 = close commissioning

      Events~

      Events published as JSON MQTT that can be captured in rules:

      {"Matter":{"Initialized":1}}
      when the device is configured (all endpoints created). Can be used in rules with trigger Matter#Initialized

      {"Matter":{"Commissioning":1,"PairingCode":"00054912336","QRCode":"MT:Y.K90IRV01YZ.548G00"}}
      when commissioning is open

      {"Matter":{"Commissioning":0}}
      when commissioning is closed

      For developers~

      Developers might be interested in understanding the Tasmota Matter Internals

      \ No newline at end of file diff --git a/Modbus-Bridge/index.html b/Modbus-Bridge/index.html new file mode 100644 index 0000000000..f1563e8063 --- /dev/null +++ b/Modbus-Bridge/index.html @@ -0,0 +1,15 @@ + Modbus Bridge - Tasmota
      Skip to content

      Modbus Bridge~

      Add a "Modbus bridge" functionality to a device that is otherwise serial Modbus RTU only

      This feature is only included in tasmota32 binary

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_MODBUS_BRIDGE
      +#define USE_MODBUS_BRIDGE       // Add support for software Modbus Bridge (+3k code)
      +#define USE_MODBUS_BRIDGE_TCP   // Add support for software Modbus TCP Bridge (Must also enable USE_MODBUS_BRIDGE)
      +#endif
      +

      In most cases you'll need an RS485 converter like this:

      rs485 converter

      Introduction~

      Connect the Modbus device to an ESP and Tasmota will create a bridge to the Modbus network.

      The Modbus Bridge driver features 2 kind of bridges.

      USE_MODBUS_BRIDGE: The bridge can be used by commands in the console and via MQTT messages.
      USE_MODBUS_BRIDGE_TCP: The bridge can be used by commands in the console and via MQTT messages but also as Modbus TCP/IP bridge

      Configuration~

      First assign two GPIOs to ModBR Tx and ModBR Rx in the "Configure Module" page. The Rx/Tx are relative to the ESP device. For example with ESP-12's hardware serial, set GPIO1 as ModBR Tx and GPIO3 as ModBR Rx.

      Then set baud rate with ModbusBaudRate (default is 9600).

      Commands~

      Command Parameters
      ModbusSend Sending a message to the Modbus network as JSON payload.
      {"deviceAddress":<value>, "functionCode":<value>, "startAddress":<value>, "type":"<value>","count":<value> "endian":<value>}
        "deviceAddress":1..255 = device address from the Modbus slave.
        "functioncode":1..6 or 15..16 = function code to send to the Modbus slave (see table below).
        "startaddress":1..65535 address of the first register to read.
        "type":"<value>" Gives the type of the returned data (see table below).
        "count":1..n the number of values to be requested.
        "endian":msb,lsb Sets endian msb (big) or lsb (little) (optional).
      ModbusBaudrate 1200...115200 = set baudrate for serial (only 8N1 mode) in 1200 increments (default = 9600)
      ModbusSerialConfig Set serial protocol using data/parity/stop conventional notation (example: 8N1 or 702)
      0..23 = set serial protocol (3 equals 8N1)

      FunctionCode~

      Function Code Description
      1 Read Coils
      2 Read Discrete Inputs
      3 Read Multiple Holding Registers
      4 Read Input Registers
      5 Write Single Coil
      6 Write Single Register
      15 Write Multiple Coils
      16 Write Multiple Registers

      Type~

      Type Description
      raw Return or send the slave data as a raw values
      bit Return or send the slave data as a bit values
      hex Return or send the slave data as a hex values
      float Return or send the slave data as floats
      uint8 Return or send the slave data as an 8 bits unsigned int
      uint32 Return or send the slave data as a 32 bits unsigned int
      uint16 Return or send the slave data as a 16 bits unsigned int
      uint32 Return or send the slave data as a 32 bits unsigned int
      int8 Return or send the slave data as an 8 bits signed int
      int16 Return or send the slave data as a 16 bits signed int
      int32 Return or send the slave data as a 32 bits signed int

      Additional commands for USE_MODBUS_BRIDGE_TCP~

      Command Parameters
      ModbusTCPStart <port> = start the Modbus TCP bridge on the specified port
      ModbusTCPConnect <port>,<ip_address> = connect to a remote Modbus TCP server on remote tcp port˙ and ip_address
      ModbusTCPMqtt <x> = send results queried by tcp modbus client also to mqtt when parameter is 1

      Returned Data~

      {"ModbusReceived":{"DeviceAddress":<value>,"FunctionCode":<value>,"StartAddress":<value>,"Length":<value>,"Count":<value>,"Values":[value1,value2,value3,valueN]}}
      +

      In raw mode, only the data is returned, no other fields.

      Error Codes~

      There are 2 types of errors, errors from the tasmotamodbus driver and errors from this Modbusbridge module.

      Module Errors~

      Errors from this Modbus bridge module can be recognized by MBS: MBR Send error or MBS: MBR Recv error at the start of the errormessage.

      Errorcode Description
      1 nodataexpected
      2 wrongdeviceaddress
      3 wrongfunctioncode
      4 wrongstartaddress
      5 wrongtype
      6 wrongregistercount
      7 wrongcount
      8 tomanydata

      Driver Errors~

      Errors from the tasmotamodbus driver can be recognized by MBS: MBR Driver error

      Errorcode Description
      1 Illegal Function
      2 Illegal Data Address
      3 Illegal Data Value
      4 Slave Error
      5 Acknowledge but not finished (no error)
      6 Slave Busy
      7 Not enough minimal data received
      8 Memory Parity error
      9 Crc error
      10 Gateway Path Unavailable
      11 Gateway Target device failed to respond
      12 Wrong register count
      13 Register data not specified

      Example of use~

      Requesting 4 holding registers starting from register 1 from slave address

      Register 1~

      On command:

      ModBusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 1, "type":"uint16", "count":4}
      +

      Response:

      RSL: RESULT = {"ModbusSend":"Done"}
      +RSL: RESULT = {"ModbusReceived":{"DeviceAddress":1,"FunctionCode":3,"StartAddress":1,"Length":13,"Count":4,"Values":[65282,65028,65280,65024]}}
      +

      Set coil register 1 of slaveaddress 1 to ON:~

      On command:

      ModBusSend {"deviceaddress": 1, "functioncode": 5, "startaddress": 1, "type":"bit", "count":1, "values":[1]}
      +
      Response:
      RSL: RESULT = {"ModbusSend":"Done"}
      +RSL: RESULT = {"ModbusReceived":{"DeviceAddress":1,"FunctionCode":5,"StartAddress":1,"Length":8,"Count":1,"Values":[255]}}
      +

      Setting multiple coils starting from coil register 1 from slave address 1~

      On command:

      ModBusSend {"deviceaddress": 1, "functioncode": 15, "startaddress": 1, "type":"bit", "count":8, "values":[1,0,0,1,1,1,0,0]}
      +
      Response:
      RSL: RESULT = {"ModbusSend":"Done"}
      +RSL: RESULT = {"ModbusReceived":{"DeviceAddress":1,"FunctionCode":15,"StartAddress":1,"Length":8,"Count":1}}
      +

      Additional resources~

      Tasmota Pull Request
      Modbus Protocol
      More Modbus information

      \ No newline at end of file diff --git a/Modules/index.html b/Modules/index.html new file mode 100644 index 0000000000..198b47af0c --- /dev/null +++ b/Modules/index.html @@ -0,0 +1 @@ + Modules - Tasmota

      Modules

      Module is a firmware supported device which has specific code to enable its features.

      Configure Module page in the webUI is used to configure additional components connected to one of the free GPIO pins of the device.

      Warning

      Use Templates to configure Tasmota for your device if it doesn't exist in the module list. New Modules are added to Tasmota only if a device requires additional code for new functions.

      Tip

      Use Generic module Module 18 to have almost all GPIO pins available.

      Each module is assigned a number which is used in Template configuration or when using Module command.

      # Name Module specifics
      0 Template Module for currently active template. Named after the template NAME field.
      If a template is not active it will display Generic (0).
      Do not use it until you configure a template, use Generic (18) instead
      1 Sonoff Basic
      2 Sonoff RF
      3 Sonoff SV
      4 Sonoff TH
      5 Sonoff Dual Process relay and button via hardware serial interface using GPIO01 and GPIO03. Change the baud rate to 19200 bps. Process buttons as single press only
      6 Sonoff POW
      7 Sonoff 4Ch
      8 Sonoff S2X
      9 Slampher
      10 Sonoff Touch Invert LedState 1 functionality
      11 Sonoff LED Set light type to 2 PWM channels disregarding SetOption15. Fix device specific LED instabilities by disabling GPIO04, GPIO5 and GPIO14
      12 1 Channel
      13 4 Channel See Sonoff Dual
      14 Motor C/AC Force all relays ON at power up and disable command PowerOnState
      15 ElectroDragon
      16 EXS Relay(s) Enable pulse latching using even/odd numbered relay pairs
      17 WION
      18 Generic Show Wemos specific pin information in GUI
      19 Sonoff Dev
      20 H801 Change hardware UART Tx from GPIO01 to GPIO02
      21 Sonoff SC Enable and process data via hardware serial interface using GPIO01 and GPIO03. Change the baud rate to 19200 bps
      22 Sonoff BN-SZ Set light type to 1 PWM channel disregarding SetOption15
      23 Sonoff 4Ch Pro Button handling disregarding SetOption13 only allowing single press to enable RF learning while holding the button
      24 Huafan SS
      25 Sonoff Bridge Enable and Process data via hardware serial interface using GPIO01 and GPIO03. Change the baud rate to 19200 bps. Process 16 buttons in web GUI. Enable EFM8BB1 firmware upload
      26 Sonoff B1 Set light type to RGBWC using MY92x1
      27 Ailight Set light type to RGBW using MY92x1
      28 Sonoff T1 1Ch See Sonoff Touch
      29 Sonoff T1 2Ch See Sonoff Touch
      30 Sonoff T1 3Ch See Sonoff Touch
      31 Supla Espablo
      32 Witty Cloud
      33 Yunshan Relay
      34 MagicHome
      35 Luani HVIO
      36 KMC 70011
      37 Arilux LC01
      38 Arilux LC11
      39 Sonoff Dual R2 Process buttons as single press only
      40 Arilux LC06
      41 Sonoff S31 Selects component types for the CSE7766 (serial connected energy monitoring chip) with Rx and Tx hardware serial (even parity) on GPIO01 and GPIO03 respectively. Sets serial interface to 4800 baud and disables serial logging
      42 Zengge WF017
      43 Sonoff Pow R2
      44 Sonoff IFan02 Enable command FanSpeed. Disable Interlock and PulseTime. Tune status information, MQTT data and GUI. Sync with microcontroller. Process Domoticz Fan state
      45 Blitzwolf SHP Module specific power monitoring calibration
      46 Shelly 1
      47 Shelly 2
      48 Xiaomi Philips Process color temperature using PWM2 and intensity using PWM1
      49 Neo Coolcam
      50 ESP SwitCh
      51 Obi Socket
      52 Teckin
      53 APLIC WDP303075
      54 TuyaMCU Enable and process data via software or hardware serial interface using component 107 and 108. Change the baud rate to 9600 bps. Process all buttons. Read more...
      55 Gosund SP1 v23
      56 Armtronix Dimmers Enable and process data via software or hardware serial interface using component 148 and 149. Change baudrate to 115200 bps.
      57 SK03 Outdoor (Tuya)
      58 PS-16-DZ Enable and process data via software or hardware serial interface using component 148 and 149. Change the baud rate to 19200 bps.
      59 Teckin US
      60 Manzoku Strip (EU 4)
      61 Obi Socket 2
      62 YTF IR Bridge Disable serial interface to stop loopback.
      63 Digoo DG-SP202
      64 KA10
      65 Luminea ZX2820
      66 Mi Desk Lamp Process rotary and Button1 data specific to this device
      67 SP10
      68 WAGA CHCZ02MB
      69 SYF05
      70 Sonoff L1
      71 Sonoff iFan03
      72 EX-Store Dimmer
      73 PWM Dimmer For MJ-SD01/acenx/NTONPOWER PWM dimmers. Read more...
      74 Sonoff D1 Dimmer
      75 Sonoff ZBBridge Sonoff Zigbee bridge

      Serial logging is disabled by the Tasmota code for several modules and components (e.g., Sonoff POW, Sonoff S31, Sonoff Dual (v1), Tuya dimmers, PZEM components, etc.). Serial communication is used by these devices to transfer the data from the MCU chip to the ESP chip. Do not enable serial logging (SerialLog 0) on these devices. It can cause the device software to crash.

      Supported Modules

      \ No newline at end of file diff --git a/Moisture-Sensor-and-Chirp!-Sensor/index.html b/Moisture-Sensor-and-Chirp!-Sensor/index.html new file mode 100644 index 0000000000..afec6f4122 --- /dev/null +++ b/Moisture-Sensor-and-Chirp!-Sensor/index.html @@ -0,0 +1,4 @@ + Moisture Sensor and Chirp! Sensor - Tasmota
      Skip to content

      Moisture Sensor and Chirp! Sensor

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_CHIRP
      +#define USE_CHIRP        // [I2cDriver33] Enable CHIRP soil moisture sensor (variable I2C address, default 0x20)
      +#endif
      +

      Chirp! I2C moisture sensor is the sensor-only version of the original Chirp! sensor.

      The "sensor mode only" (without the chirp function) is the preferred sensor variant for Tasmota. It provides additional temperature readings. Chirp! is a plant watering alarm which uses capacitive sensing to measure moisture. It provides ambient light readings and works in Tasmota, but is not the recommended version.

      Additional References: - Catnip electronics
      - Plant Watering Alarm

      Connecting to an ESP82xx~

      Use a standard I2C connection plus 3.3V and GND.

      Device Configuration~

      In the Configuration -> Configure Other page, enter and activate the following template:
      {"NAME":"HW-655 PZEM","GPIO":[0,0,0,0,6,5,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}

      BASE: Generic (18) GPIO4 : I2C SDA (06) GPIO5 : I2C SCL (05)

      At boot time the driver will scan the I2C bus for CHIRP moisture sensors and enumerate them starting with 0. The sensor has a default I2C address of 0x20, which can be changed within the driver so that multiple sensors are possible.

      Write access to the sensor is potentially dangerous!! Only change the I2C address while only one is sensor connected, using a stable power supply, and if you are familiar with how to flash the sensor.

      Commands~

      Command Description
      CHIRPScan Re-scan the I2C bus and re-enumerate the sensors.
      CHIRPSelect Select the active sensor, which can receive commands. To select the first sensor use CHIRPSELECT 0.
      CHIRPSet Set the new I2C address for the selected sensor. Use decimal address.
      To change active sensor to 0x1f (=31) use CHIRPSET 31.
      CHIRPSleep Put the selected sensor into sleep mode.
      CHIRPWake Wake the selected (sleeping) sensor.
      CHIRPReset Reset the selected sensor.

      Sensor readings~

      The original explanation from the manufacturer can be found on this tindie page .

      It is important to understand, that the light sensor does not provide LUX but a relative reading (0..65535), where more light means a lower value! The term DARKNESS is used in Tasmota.

      !! ⚠️ PLEASE USE TelePeriod OF 20 OR GREATER ⚠️ !!
      The driver will sync with the TelePeriod and start the measure cycle about 17 seconds before the next telemetry message. You can issue a Status 8 and any time to output that last sensor readings.

      Known issues~

      The sensor is relatively slow and therefore the driver will (try to) slow down the I2C bus-speed and extend the CLOCKSTRETCHLIMIT. A long discussion about it can be found here. The problem seems to occur mostly when the sensor wakes up from sleep. That is why the implemented auto-sleep-wake function is currently deactivated in the driver. Typically when you get readings of 0 for all 3 measurements, then the I2C bus is likely "frozen". The expected result for I2CSCAN on the console is {"I2CScan":"Error 4 at 0x01"}. A restart (Restart 1) of Tasmota should be enough, but you may need to power cycle the device. Different ESP Cores may lead to different behavior.

      It is possible to flash incorrect firmware to a sensor (chirp vs non-chirp). This will very likely lead to nonsense temperature readings.

      Multiple sensors on one I2C bus were tested successfully, but are not guaranteed to work due to multiple possible reasons (power, cabling, ...). Your mileage may vary.

      \ No newline at end of file diff --git a/NRF24L01/index.html b/NRF24L01/index.html new file mode 100644 index 0000000000..04b0b6fdce --- /dev/null +++ b/NRF24L01/index.html @@ -0,0 +1,9 @@ + NRF24L01 module - Tasmota
      Skip to content

      NRF24L01 module~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #define USE_SPI            // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC)
      +#ifndef USE_NRF24
      +#define USE_NRF24          // Add SPI support for RF24L01(+) (+2k6 code)
      +#define USE_MIBLE          // BLE-bridge for some Mijia-BLE-sensors (+4k7 code)
      +#endif
      +

      This chip is manufactured by Nordic Semiconductors as a single chip transceiver in the 2,4 GHz band. There are many applications of this chip in many projects as a versatile very low cost wireless module.

      In recent years solutions were found to use this chip for limited Bluetooth-Low-Energy communication. One of the first articles about this topic can be found here.

      Subsequently, further work was done by several developers and a working bridge to read sensor data from a Xiaomi MJ_HT_V1 BLE sensor was created. The fundamental principle is, that some of these sensors send its data as a usual BLE-advertisement packet with a proprietary data format at the end of the payload. These packets had to fit into the 32 bytes of the FIFO-RX-buffer of the RF24L01, otherwise the "later" bytes were lost.

      A new solution was found for the Tasmota driver.

      The basic idea is to use some constant ID-bytes of the BLE-packets as the PDU-type for the NRF24l01. Thus, all bytes before these ID-bytes are lost and the size restriction for the payload is successfully circumvented. That way it is possible to read the sensor data from a Mi Flora sensor, which is positioned outside of the 32-byte-range. Of course there is still no bidirectional "real" BLE-communication, only advertisements can be read.

      Configuration~

      Wiring~

      Configure the pins for SPI_DC and SPI_CS while connecting the hardware SPI pins 12 - 14 (MOSI, MISO and CLOCK).

      Tip

      In order to simplify the code, the pin names from the SPI-display-drivers are used in the webUI! For nRF24L01 SPI_DC translates to CSN and SPI_CS to CE.

      Warning

      Even slightly loose cables can lead to malfunctions of the SPI-data-transfer. This can produce a Software WDT reset.

      Tasmota Settings~

      No additional steps are necessary.

      The initial log should like this:

      00:00:00 NRF24L01 initialized  
      +00:00:00 NRF24L01+ detected  
      +00:00:00 MIBLE: started  
      +

      The driver will do the rest automatically and start to look for known "special" packets, which will be used to extract the sensor readings. webUI and TELE-messages will be populated with sensor data. This can take a while after start and may be influenced by the general traffic on the 2,4 GHz band.

      For a complete overview of supported devices, commands and features read the Bluetooth article.

      \ No newline at end of file diff --git a/NeoPool/index.html b/NeoPool/index.html new file mode 100644 index 0000000000..664b23b105 --- /dev/null +++ b/NeoPool/index.html @@ -0,0 +1,436 @@ + Sugar Valley NeoPool Controller - Tasmota
      Skip to content

      Sugar Valley NeoPool Controller~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_NEOPOOL
      +#define USE_NEOPOOL                       // Add support for Sugar Valley NeoPool Controller - also known under brands Hidrolife, Aquascenic, Oxilife, Bionet, Hidroniser, UVScenic, Station, Brilix, Bayrol and Hay (+6k flash, +60 mem)
      +#endif
      +

      Sugar Valley NeoPool are water treatment systems also known under the names Hidrolife, Aquascenic, Oxilife, Bionet, Hidroniser, UVScenic, Station, Brilix, Bayrol and Hay. It uses a RS485 interface with the Modbus data protocol for enhancement equipment like Wifi-Interface or a second attached control panel. All functions and parameters can be queried and controlled via this bus interface.

      The Tasmota Sugar Valley NeoPool Controller sensor module shows the most of parameters such as the built-in display:

      There are Tasmota commands implemented to control the high level functions for filtration, light and system parameters such as pH set point, hydrolysis level, redox set point etc. However, the sensor also provides low-level commands to directly read and write NeoPool register, means that you have the option to implement your own commands via home automation systems or by using the Tasmota build-in possibilities Rules with Backlog or the powerful Berry language on ESP32.

      Connection~

      The NeoPool controller uses a RS485 interface, the ESP has RS232 interfaces. Both are serial interfaces but with different physical specifications. Therefore to connect your NeoPool controller to an ESP82xx/32 you need a TTL-UART to RS485 converter. It is recommended to use GPIO1 and GPIO3 on ESP8266 side, since the ESP then uses the hardware serial interface.

      The following TTL UART to RS485 converter have been tested with both an ESP8266 and ESP32 using a Vcc of 3.3V:

      Note

      Your TTL UART to RS485 converter must be able to work with an operating voltage of 3.3V. Some converters are not designed for operating with 3.3V and only works with 5V TTL level - these converters are useless. Do not operate your TTL UART to RS485 converter with 5V, your converter must be operated with the 3.3V from ESP, otherwise the ESP GPIO ports will be damaged.

      The Sugar Valley NeoPool RS485 connector pins are located under the connection cover, for the Sugar-Valley products on the right-hand side next to the relay connections:

      The pin assignment (from top to bottom):

      Pin Description
      1 +12V
      2 nc
      3 Modbus A+
      4 Modbus B-
      5 Modbus GND

      The +12V connection is the 12V from the internal power supply, do not feed in any external voltage.

      You can use the "WIFI" or "EXTERN" connector, both are independent Modbus channels and uses the Modbus address 1 by default.

      Note

      The "DISPLAY" port can only be used if neither the built-in nor an external display is connected but since there is probably at least one display connected to one of the two "DISPLAY" ports, the "DISPLAY" port is useless.

      Using WIFI Port~

      Using EXTERN Port~

      Note

      Leave the define for NEOPOOL_MODBUS_ADDRESS set to 1 whether you are using the "WIFI" or "EXTERNAL" port (unless you have changed the parameters for it within your Sugar Valley device).

      Configuration~

      Tasmota settings~

      If you followed the recommendations above, the two GPIOs will be assigned as follows under Tasmota Configuration -> Configure Module:

      • first change Module type to Generic (0) - this will restart your Tasmota

      After restart set * GPIO1 to NeoPool RX * GPIO3 to NeoPool TX

      so it looks like this

      Don't be surprised that Rx seems to be connected to Tx here (and vice versa). The Rx and Tx designations are to be considered from the point of view of the respective devices, which can be confusing.

      After Tasmota restarts, the main screen should display the controller data as shown above. If not, check that the A+/B pins aren't swapped and that the Rx/Tx pins are on the correct GPIOs - swap once if in doubt.

      SENSOR data~

      Sensor data is sent via the Tasmota topic tele/%topic%/SENSOR in JSON format every TelePeriod interval. To get the data immediately, use the Tasmota TelePeriod command without parameter:

      {
      +  "Time": "2021-06-01T11:00:00+02:00",
      +  "NeoPool": {
      +    "Time": "2021-06-01T11:00:00",
      +    "Type": "Oxilife",
      +    "Module": {
      +      "pH": 1,
      +      "Redox": 1,
      +      "Hydrolysis": 1,
      +      "Chlorine": 1,
      +      "Conductivity": 1,
      +      "Ionization": 1
      +    },
      +    "Temperature": 23.5,
      +    "Power": {
      +      "Version": "V3.45",
      +      "NodeID": "2A55 6E6B 6E6F 776E 2049 442A",
      +      "5V": 5.017,
      +      "12V": 13.904,
      +      "24-30V": 33.721,
      +      "4-20mA": 0.01
      +    },
      +    "pH": {
      +      "Data": 7.2,
      +      "Min": 7.0,
      +      "Max": 7.2,
      +      "State": 0,
      +      "Pump": 2,
      +      "FL1": 0,
      +      "Tank": 1
      +    },
      +    "Redox": {
      +      "Data": 752,
      +      "Setpoint": 750
      +    },
      +    "Chlorine": {
      +      "Data": 0.7,
      +      "Setpoint": 1.0
      +    },
      +    "Conductivity": 0,
      +    "Ionization": {
      +      "Data": 0,
      +      "Setpoint": 0,
      +      "Max": 0
      +    },
      +    "Hydrolysis": {
      +      "Data": 100,
      +      "Unit": "%",
      +      "Runtime": {
      +        "Total": "28T22:13:19",
      +        "Part": "28T22:13:02",
      +        "Pol1": "14T12:32:46",
      +        "Pol2": "14T09:40:33",
      +        "Changes": 258
      +      },
      +      "State": "Pol1",
      +      "Cover": 0,
      +      "Boost": 0,
      +      "Low": 0
      +    },
      +    "Filtration": {
      +      "State": 1,
      +      "Speed": 2,
      +      "Mode": 1
      +    },
      +    "Light": 0,
      +    "Relay": {
      +      "State": [0, 1, 0, 0, 0, 1, 0],
      +      "Aux": [0, 0, 1, 0],
      +      "Acid": 0
      +    }
      +  },
      +  "TempUnit": "C"
      +}
      +

      The JSON values "pH", "Redox", "Hydrolysis", "Chlorine", "Conductivity" and "Ionization" are only available if the corresponding module is installed in the device (the corresponding "Module" subkey must be 1 ).

      To check which modules are installed use the "Module" value from SENSOR topic or query it manually by using the NPControl command:

      {
      +  "Modules": {
      +    "pH": 1,
      +    "Redox": 1,
      +    "Hydrolysis": 1,
      +    "Chlorine": 0,
      +    "Conductivity": 0,
      +    "Ionization": 0
      +  },
      +  "Relay": {
      +    "Acid": 1,
      +    "Base": 0,
      +    "Redox": 0,
      +    "Chlorine": 0,
      +    "Conductivity": 0,
      +    "Heating": 0,
      +    "UV": 0,
      +    "Valve": 0
      +  }
      +}
      +

      Commands~

      This sensor supports some high-level commands for end user.

      Regardless, all other Modbus registers can be read and write, so you can enhance your Sugar Valley control by using low-level NPRead/NPWrite commands.

      Modbus register addresses and their meaning are described within source file xsns_83_neopool.ino at the beginning and (partly) within document 171-Modbus-registers.
      Please note that Sugar Valley Modbus registers are not byte addresses but modbus registers containing 16-bit values - don't think in byte memory layout.

      Command Parameters
      NPFiltration {<state> {speed}}
      get/set manual filtration (state = 0 or 1, speed = 1..3). Get if state is omitted, otherwise set accordingly <state>:
      • 0 - manual turn filtration pump off
      • 1 - manual turn filtration pump on
      optional speed control is possible for non-standard filtration types:
      • 1 - slow
      • 2 - medium
      • 3 - fast
      NPFiltrationmode {<mode>}
      get/set filtration mode (mode = 0..4 or 13). Get if mode is omitted, otherwise set accordingly <mode>:
      • 0 - MANUAL
        allows to turn the filtration (and all other systems that depend on it) on and off
      • 1 - AUTO
        allows filtering to be turned on and off according to the settings of the MBF_PAR_TIMER_BLOCK_FILT_INT timers.
      • 2 - HEATING
        similar to the AUTO mode, but includes setting the temperature for the heating function. This mode is activated only if the BF_PAR_HEATING_MODE register is at 1 and there is a heating relay assigned.
      • 3 - SMART
        adjusts the pump operating times depending on the temperature. This mode is activated only if the MBF_PAR_TEMPERATURE_ACTIVE register is at 1.
      • 4 - INTELLIGENT
        performs an intelligent filtration process in combination with the heating function. This mode is activated only if the MBF_PAR_HEATING_MODE register is at 1 and there is a heating relay assigned.
      • 13 - BACKWASH
        started when the backwash operation is activated.
      NPTime {<time>}
      get/set device time. Get if time is omitted, otherwise set device time accordingly <time>:
      • 0 - sync with Tasmota local time
      • 1 - sync with Tasmota utc time
      • 2..4294967295 - set time as epoch
      NPLight {<state> {delay}}
      get/set light (state = 0..4, delay = 5..100 in 1/10 sec). Get if state is omitted, otherwise set accordingly <state>:
      • 0 - manual turn light off
      • 1 - manual turn light on
      • 2 - manual toggle light
      • 3 - switch light into auto mode according MBF_PAR_TIMER_BLOCK_LIGHT_INT settings
      • 4 - select light RGB LED to next program. This is normally done by power the light on (if currently off), then power off the light for a given time (delay) and power on again. The default delay is 15 (=1.5 sec).
      NPpHMin {<ph>}
      (only available if pH module is installed)
      get/set pH lower limit (ph = 0..14)
      get current limit if is omitted, otherwise set.
      NPpHMax {<ph>}
      (only available if pH module is installed)
      get/set pH upper limit (ph = 0..14)
      get current limit if is omitted, otherwise set.
      NPpH {<ph>}
      (only available if pH module is installed)
      get/set pH upper limit (ph = 0..14)
      same as NPpHMax
      NPRedox {<setpoint>}
      (only available if redox module is installed)
      get/set redox set point in mV (setpoint = 0..100, the upper limit of the range may vary depending on the MBF_PAR_HIDRO_NOM register)
      get current set point if is omitted, otherwise set
      NPHydrolysis {<level>}
      (only available if hydrolysis/electrolysis control is present)
      get/set hydrolysis/electrolysis level in % (level = 0..100)
      get current level if is omitted, otherwise set
      NPIonization {<level>}
      (only available if ionization control is present)
      get/set ionization target production level (level = 0..x, the upper limit x of the range may vary depending on the MBF_PAR_ION_NOM register)
      get current level if is omitted, otherwise set
      NPChlorine {<setpoint>}
      (only available if free chlorine probe detector is installed)
      get/set chlorine set point in ppm (setpoint = 0..10)
      get current set point if is omitted, otherwise set
      NPControl
      Show information about system controls
      NPOnError {<repeat>}
      get/set auto-repeat Modbus read/write commands on error (repeat = 0..10). Get if repeat is omitted, otherwise set accordingly <repeat>:
      • 0 - disable auto-repeat on read/write error
      • 1..10 - repeat commands n times until ok
      NPResult {<format>}
      get/set addr/data result format for read/write commands (format = 0|1). Get if format is omitted, otherwise set accordingly <format>:
      • 0 - output decimal numbers
      • 1 - output hexadecimal strings, this is the default
      NPPHRes {<digits>}
      get/set number of digits in results for PH value (digits = 0..3).
      NPCLRes {<digits>}
      get/set number of digits in results for CL value (digits = 0..3).
      NPIONRes {<digits>}
      get/set number of digits in results for ION value (digits = 0..3).
      NPRead <addr> {<cnt>}
      read 16-bit register (addr = 0..0x060F, cnt = 1..30). cnt = 1 if omitted
      NPReadL <addr> {<cnt>}
      read 32-bit register (addr = 0..0x060F, cnt = 1..15). cnt = 1 if omitted
      NPWrite <addr> <data> {<data>...}
      write 16-bit register (addr = 0..0x060F, data = 0..0xFFFF). Use of data max 10 times
      NPWriteL <addr> <data> {<data>...}
      write 32-bit register (addr = 0..0x060F, data = 0..0xFFFFFFFF). Use of data max 10 times
      NPBit <addr> <bit> {<data>}
      read/write a 16-bit register single bit (addr = 0..0x060F, bit = 0..15, data = 0|1). Read if data is omitted, otherwise set single bit
      NPBitL <addr> <bit> {<data>}
      read/write a 32-bit register single bit (addr = 0..0x060F, bit = 0..31, data = 0|1). Read if data is omitted, otherwise set single bit
      NPEscape clears possible errors (like pump exceeded time etc.)
      NPExec take over changes without writing to EEPROM. This command is necessary e.g. on changes in Installer page (addr 0x0400..0x04EE).
      NPSave write data permanently into EEPROM.
      During the EEPROM write procedure the NeoPool device may be unresponsive to MODBUS requests, this process always takes less than 1 second.
      Since this process is limited by the number of EEPROM write cycles, it is recommend to write all necessary changes to the registers and only then execute EEPROM write process using this command.
      Note: The number of EEPROM writes for Sugar Valley NeoPool devices is guaranteed 100,000 cycles. As soon as this number is exceeded, further storage of information can no longer be guaranteed.

      Examples~

      Example

      Get filtration mode

      NPFiltrationmode
      +RESULT = {"NPFiltrationmode":"Manual"}
      +

      Example

      Set filtration mode

      NPFiltrationmode 1
      +{"NPFiltrationmode":"Auto"}
      +

      Example

      Enable hydrolysis boost mode without redox control

      To do this, write 0x85A0 to register MBF_BOOST_CTRL (0x020C), exec, save it and notify system using register MBF_NOTIFICATION (0x0110) about changes:

      Backlog NPWrite 0x020C,0x85A0;NPSave;NPExec;NPWrite 0x0110,0x7F
      +RESULT = {"NPWrite":{"Address":"0x020C","Data":"0x85A0"}}
      +RESULT = {"NPSave":"Done"}
      +RESULT = {"NPExec":"Done"}
      +RESULT = {"NPWrite":{"Address":"0x0110","Data":"0x0000"}}
      +

      Example

      Disable hydrolysis boost mode

      To do this, write 0 to register MBF_BOOST_CTRL (0x020C), exec, save it and notify system using register MBF_NOTIFICATION (0x0110) about changes:

      Backlog NPWrite 0x020C,0;NPSave;NPExec;NPWrite 0x0110,0x7F
      +RESULT = {"NPWrite":{"Address":"0x020C","Data":"0x0000"}}
      +RESULT = {"NPSave":"Done"}
      +RESULT = {"NPExec":"Done"}
      +RESULT = {"NPWrite":{"Address":"0x0110","Data":"0x0000"}}
      +

      Example

      Switch light relay on

      NPLight 1
      +RESULT = {"NPLight":"ON"}
      +

      Example

      Read Heating setpoint temperature

      Here we read register MBF_PAR_HEATING_TEMP (0x0416):

      Backlog NPResult 0;NPRead 0x416
      +RESULT = {"NPResult":0}
      +RESULT = {"NPRead":{"Address":1046,"Data":28}}
      +

      Example

      Enable additional factory menu

      For that enable bit MBMSK_SHOW_FACTORY_MENU (15) in register MBF_PAR_UICFG_VISUAL_OPTIONS (0x0605)

      Backlog NPBit 0x605,15,1;NPSave
      +RESULT = {"NPBit":{"Address":"0x0605","Data":"0xAFC0","Bit15":1}}
      +RESULT = {"NPSave":"Done"}
      +

      Example

      Read system time

      We either use command NPTime or read the 32-bit value starting MBF_PAR_TIME_LOW (0x0408) using decimal output:

      Backlog NPResult 0;NPTime;NPReadL 0x408
      +RESULT = {"NPResult":0}
      +RESULT = {"NPTime":"2021-01-31T21:22:20"}
      +RESULT = {"NPReadL":{"Address":1032,"Data":1612124540}}
      +

      Example

      Enable temperature module

      Do this by enabling MBF_PAR_TEMPERATURE_ACTIVE (0x04F) and set it permanently in EEPROM::

      Backlog NPWrite 0x40F,1;NPSave
      +RESULT = {"NPWrite":{"Address":"0x040F","Data":"0x0001"}}
      +RESULT = {"NPSave":"Done"}
      +

      Example

      Hide auxiliary relay display from main menu

      To do this, set bit MBMSK_HIDE_AUX_RELAYS (3) in register MBF_PAR_UICFG_VISUAL_OPTIONS (0x0605):

      NPBit 0x605,3,1
      +RESULT = {"NPBit":{"Address":"0x0605","Data":"0x08C8"}}
      +

      Example

      Read Filtration interval 1-3 settings

      To do this, we read the registers MBF_PAR_TIMER_BLOCK_FILT_INT1 (0x0434), MBF_PAR_TIMER_BLOCK_FILT_INT2 (0x0443) and MBF_PAR_TIMER_BLOCK_FILT_INT3 (0x0452) with offset MBV_TIMER_OFFMB_TIMER_ENABLE (0) as 16-bit values and the remaining timer offset values MBV_TIMER_OFFMB_* as 32-bit values:

      Backlog NPResult 0;NPRead 0x434;NPReadL 0x435,7;NPRead 0x443;NPReadL 0x444,7;NPRead 0x452;NPReadL 0x0453,7
      +RESULT = {"NPResult":0}
      +RESULT = {"NPRead":{"Address":1076,"Data":1}}
      +RESULT = {"NPReadL":{"Address":1077,"Data":[28800,0,86400,14400,0,1,0]}}
      +RESULT = {"NPRead":{"Address":1091,"Data":1}}
      +RESULT = {"NPReadL":{"Address":1092,"Data":[43200,0,86400,21600,0,1,0]}}
      +RESULT = {"NPRead":{"Address":1106,"Data":1}}
      +RESULT = {"NPReadL":{"Address":1107,"Data":[0,0,86400,0,0,1,0]}} *
      +

      Example

      Set filtration interval

      Here we set interval 1 to a daily interval between 9:00 - 12:30 (9:00: 3600 * 9 ≙ 32400 / 12:30 ≙ 3,5h = 12600)

      For this write register MBF_PAR_TIMER_BLOCK_FILT_INT1 (0x0434) using the offsets MBV_TIMER_OFFMB_. For the sake of simplicity we write 4 consecutive 32-bit registers:

      • MBV_TIMER_OFFMB_TIMER_ON: Timer start = 93600 + 0060 = 32400
      • MBV_TIMER_OFFMB_TIMER_OFF: Timer stop - not used
      • MBV_TIMER_OFFMB_TIMER_PERIOD: Time in seconds between starting points = 86400 (means daily interval)
      • MBV_TIMER_OFFMB_TIMER_INTERVAL: Time in seconds that the timer has to run when started. This is the difference between 12:30 (123600 + 3060 = 45000) and 9:30(see Timer start = 32400) = 12600
      NPWriteL 0x435,32400 0 86400 12600
      +RESULT = {"NPWriteL":{"Address":1077,"Data":[32400,0,86400,12600]}}
      +

      Example

      Manual switch relay 7 (Aux4)

      To switch Aux4 ON, we set MBF_PAR_TIMER_BLOCK_AUX4_INT1 (0x04D9) + MBV_TIMER_OFFMB_TIMER_ENABLE (0) to MBV_PAR_CTIMER_ALWAYS_ON (3):.

      Backlog NPWrite 0x4D9,3;NPExec
      +RESULT = {"NPWrite":{"Address":"0x04D9","Data":"0x0003"}}
      +RESULT = {"NPExec":"Done"}
      +

      To switch Aux4 OFF, we set MBF_PAR_TIMER_BLOCK_AUX4_INT1 (0x04D9) + MBV_TIMER_OFFMB_TIMER_ENABLE (0) to MBV_PAR_CTIMER_ALWAYS_OFF (4):.

      Backlog NPWrite 0x4D9,4;NPExec
      +RESULT = {"NPWrite":{"Address":"0x04D9","Data":"0x0004"}}
      +RESULT = {"NPExec":"Done"}
      +

      Example

      Modbus autorepeat on communication error

      Read current autorepeat value:

      NPOnError
      +RESULT = {"NPOnError":2}
      +

      Set autorepeat value to 3:

      NPOnError 3
      +RESULT = {"NPOnError":3}
      +

      Enhancements~

      Daily sync device to Tasmota time~

      Since the NeoPool devices, without a WiFi module, have no way of synchronizing their internal clock with an external clock and, in addition, the accuracy of the internal clock leaves something to be desired, it makes sense to synchronize the clock with Tasmota once a day. Advantageously, we do this at night after a possible daylight saving time or normal time change.

      We use a rule that synchronizes the time and which is triggered by the Tasmota built-in timer (here we use timer 10):

      Rule2
      +  ON Clock#Timer=10 DO NPTime 0 ENDON
      +

      Activate it:

      Backlog Rule2 4;Rule2 1
      +

      Configure Tasmota "Timer 10" for your needs:

      ESP82xx: Add buttons for filtration and light control~

      Add two dummy buttons to control the filtration pump and the light.

      First we define two dummy relay (which does not have any physical function) on two unused GPIO (here we use GPIO0 and GPIO4 where we define Tasmota Relay 1 and 2):

      Backlog GPIO0 224;GPIO4 225
      +

      Then we rename the buttons for better visibility:

      Backlog WebButton1 Filtration;WebButton2 Light
      +

      Now we have the WebGUI buttons like this:

      but missing the functionality behind. For that we use Rules and connect the states for Tasmota Power, Neopool filtration and light:

      Rule1
      +  ON Power1#State==0 DO NPFiltration %value% ENDON
      +  ON Power1#State==1 DO NPFiltration %value% ENDON
      +  ON NeoPool#Filtration#State==0 DO Power1 %value% ENDON
      +  ON NeoPool#Filtration#State==1 DO Power1 %value% ENDON
      +  ON Power2#State==0 DO NPLight %value% ENDON
      +  ON Power2#State==1 DO NPLight %value% ENDON
      +  ON NeoPool#Light==0 DO Power2 %value% ENDON
      +  ON NeoPool#Light==1 DO Power2 %value% ENDON
      +

      Don't wonder about the double trigger definition, which at first glance seem nonsensical - they are necessary so that the rule does not trigger endless.

      At least we activate the rule:

      Backlog Rule1 5;Rule1 1
      +

      It is important to enable the Rule ONCE (Rule1 5) function, which prevents the trigger is triggering themself in a loop.

      You can now control filtration and light using the WebGUI and get the current status of the device elements when they are switched by auto-mode or manually on the device directly.

      Additional advantage is that you can also use Tasmota Timer switching Power1 (=filtration) and Power2 (light) for your needs.

      ESP32: Adding user defined NeoPool commands to Tasmota~

      The following enhancements are made using the Berry Scripting Language which is available on ESP32 only.

      The class NeoPoolCommands below adds two new commands to Tasmota:

      Command Parameters
      NPBoost {<state>}
      get/set boost mode (state = 0..2). Get if state is omitted, otherwise set accordingly <state>:
      • 0 - disable boost mode
      • 1 - enable boost mode (without redox control)
      • 2 - enable boost mode (with redox control)
      NPAux<x> {<state>}
      get/set auxiliary relay <x> (state = 0..2). Get if state is omitted, otherwise set accordingly <state>:
      • 0 - switch off auxiliary relay
      • 1 - switch on auxiliary relay

      The class members NPBoost and NPAux can also be used as templates for further commands.

      Store the following code into a Tasmota file by using the WebGUI "Console" / "Manage File system".

      neopoolcmd.be~

      # File: neopoolcmd.be
      +#
      +# Add new commands NPBoost and NPAux
      +
      +# Neopool definitions
      +var MBF_RELAY_STATE = 0x010E
      +var MBF_NOTIFICATION = 0x0110
      +var MBF_CELL_BOOST = 0x020C
      +
      +var MBF_PAR_TIMER_BLOCK_AUX1_INT1 = 0x04AC
      +var MBF_PAR_TIMER_BLOCK_AUX2_INT1 = 0x04BB
      +var MBF_PAR_TIMER_BLOCK_AUX3_INT1 = 0x04CA
      +var MBF_PAR_TIMER_BLOCK_AUX4_INT1 = 0x04D9
      +var PAR_TIMER_BLOCK_AUX = [
      +  MBF_PAR_TIMER_BLOCK_AUX1_INT1,
      +  MBF_PAR_TIMER_BLOCK_AUX2_INT1,
      +  MBF_PAR_TIMER_BLOCK_AUX3_INT1,
      +  MBF_PAR_TIMER_BLOCK_AUX4_INT1
      +]
      +var MBV_PAR_CTIMER_ALWAYS_ON      = 3
      +var MBV_PAR_CTIMER_ALWAYS_OFF     = 4
      +
      +# NeoPool command class
      +class NeoPoolCommands
      +  var TEXT_OFF
      +  var TEXT_ON
      +  var TEXT_TOGGLE
      +
      +  # string helper
      +  def ltrim(s)
      +    import string
      +    var i = 0 while(s[i]==' ') i += 1 end
      +    return string.split(s, i)[1]
      +  end
      +  def rtrim(s)
      +    import string
      +    return string.split(s, " ")[0]
      +  end
      +  def trim(s)
      +    return self.rtrim(self.ltrim(s));
      +  end
      +
      +  # NPBoost OFF|0|ON|1|REDOX|2
      +  #   0|OFF:   Switch boost off
      +  #   1|ON:    Switch boost on without redox control
      +  #   2|REDOX: Switch boost on with redox control
      +  def NPBoost(cmd, idx, payload)
      +    import string
      +    var ctrl, parm
      +
      +    try
      +      parm = string.toupper(self.trim(payload))
      +    except ..
      +      parm = ""
      +    end
      +    if parm != ""
      +      if string.find(parm, 'OFF')>=0 || string.find(parm, self.TEXT_OFF)>=0 || string.find(parm, '0')>=0
      +        ctrl = 0
      +      elif string.find(parm, 'ON')>=0 || string.find(parm, self.TEXT_ON)>=0 || string.find(parm, '1')>=0
      +        ctrl = 0x85A0
      +      elif string.find(parm, 'REDOX')>=0 || string.find(parm, '2')>=0
      +        ctrl = 0x05A0
      +      else
      +        tasmota.resp_cmnd_error()
      +        return
      +      end
      +      tasmota.cmd(string.format("NPWrite 0x%04X,0x%04X", MBF_CELL_BOOST, ctrl))
      +      tasmota.cmd("NPSave")
      +      tasmota.cmd("NPExec")
      +      tasmota.cmd(string.format("NPWrite 0x%04X,0x7F", MBF_NOTIFICATION))
      +    else
      +      try
      +        ctrl = compile("return "..tasmota.cmd(string.format("NPRead 0x%04X", MBF_CELL_BOOST))['NPRead']['Data'])()
      +      except ..
      +        tasmota.resp_cmnd_error()
      +        return
      +      end
      +    end
      +    tasmota.resp_cmnd(string.format('{"%s":"%s"}', cmd, ctrl == 0 ? self.TEXT_OFF : (ctrl & 0x8500) == 0x8500 ? self.TEXT_ON : "REDOX"))
      +  end
      +
      +  # NPAux<x> OFF|0|ON|1 t (<x> = 1..4)
      +  #   0|OFF:    Switch Aux x to off
      +  #   1|ON:     Switch Aux x to on
      +  #   2|TOGGLE: Toggle Aux x
      +  def NPAux(cmd, idx, payload)
      +    import string
      +    var ctrl, parm
      +
      +    if idx < 1 || idx > 4
      +      tasmota.resp_cmnd_error()
      +      return
      +    end
      +
      +    try
      +      parm = string.toupper(self.trim(payload))
      +    except ..
      +      parm = ""
      +    end
      +    if parm != ""
      +      if string.find(parm, 'OFF')>=0 || string.find(parm, self.TEXT_OFF)>=0 || string.find(parm, '0')>=0
      +        ctrl = MBV_PAR_CTIMER_ALWAYS_OFF
      +      elif string.find(parm, 'ON')>=0 || string.find(parm, self.TEXT_ON)>=0 || string.find(parm, '1')>=0
      +        ctrl = MBV_PAR_CTIMER_ALWAYS_ON
      +      elif string.find(parm, 'TOGGLE')>=0 || string.find(parm, self.TEXT_TOGGLE)>=0 || string.find(parm, '2')>=0
      +        try
      +          ctrl = (compile("return "..tasmota.cmd(string.format("NPRead 0x%04X", MBF_RELAY_STATE))['NPRead']['Data'])() >> (idx+2)) & 1 ? MBV_PAR_CTIMER_ALWAYS_OFF : MBV_PAR_CTIMER_ALWAYS_ON
      +        except ..
      +          tasmota.resp_cmnd_error()
      +          return
      +        end
      +      else
      +        tasmota.resp_cmnd_error()
      +        return
      +      end
      +      tasmota.cmd(string.format("NPWrite 0x%04X,%d", PAR_TIMER_BLOCK_AUX[idx-1], ctrl))
      +      tasmota.cmd("NPExec")
      +    else
      +      try
      +        ctrl = (compile("return "..tasmota.cmd(string.format("NPRead 0x%04X", MBF_RELAY_STATE))['NPRead']['Data'])() >> (idx+2)) & 1
      +      except ..
      +        tasmota.resp_cmnd_error()
      +        return
      +      end
      +    end
      +  end
      +
      +  def init()
      +    # get tasmota settings
      +    self.TEXT_OFF = tasmota.cmd("StateText1")['StateText1']
      +    self.TEXT_ON = tasmota.cmd("StateText2")['StateText2']
      +    self.TEXT_TOGGLE = tasmota.cmd("StateText3")['StateText3']
      +    # add commands
      +    tasmota.add_cmd('NPBoost', / cmd, idx, payload -> self.NPBoost(cmd, idx, payload))
      +    tasmota.add_cmd('NPAux', / cmd, idx, payload -> self.NPAux(cmd, idx, payload))
      +  end
      +
      +  def deinit()
      +    # remove commands
      +    tasmota.remove_cmd('NPBoost')
      +    tasmota.remove_cmd('NPAux')
      +  end
      +end
      +neopoolcommands = NeoPoolCommands()
      +

      To activate the new commands, go to WebGUI "Consoles" / "Berry Scripting console" and execute

      load("neopoolcmd.be")
      +

      ESP32: Add GUI controls for filtration, light and aux relais~

      The following enhancements are made using the Berry Scripting Language which is available on ESP32 only.

      The class NeoPoolButtonMethods below adds new GUI elements to control filtration, light and aux relais:

      Store the following code into a Tasmota file by using the WebGUI "Console" / "Manage File system".

      neopoolgui.be~

      # File: neopoolgui.be
      +#
      +# Add GUI elements for filtration control, light and aux relais
      +
      +import webserver
      +import string
      +
      +class NeoPoolButtonMethods : Driver
      +
      +  #- method for adding elements to the main menu -#
      +  def web_add_main_button()
      +
      +    def selected(value, comp)
      +      return comp == value ? 'selected=""' : ''
      +    end
      +
      +    var speed = tasmota.cmd('NPFiltration')['Speed']
      +    var mode = tasmota.cmd('NPFiltrationmode')['NPFiltrationmode']
      +
      +    var html = '<p></p>'
      +
      +    # Filtration mode/speed
      +    html+= '<table style="width:100%"><tbody><tr>'
      +    html+= '  <td style="width:50%;padding: 0 4px 0 4px;">'
      +    html+= '    <label for="mode"><small>Mode:</small></label>'
      +    html+= '    <select id="mode" name="mode">'
      +    html+= string.format('<option value="m_sv_manual"%s>Manual</option>', selected(mode, 'Manual'))
      +    html+= string.format('<option value="m_sv_auto"%s>Auto</option>', selected(mode, 'Auto'))
      +    html+= string.format('<option value="m_sv_heating"%s>Heating</option>', selected(mode, 'Heating'))
      +    html+= string.format('<option value="m_sv_smart"%s>Smart</option>', selected(mode, 'Smart'))
      +    html+= string.format('<option value="m_sv_intelligent"%s>Intelligent</option>', selected(mode, 'Intelligent'))
      +    html+= '    </select>'
      +    html+= '  </td>'
      +    html+= '  <td style="width:50%;padding: 0 4px 0 4px;">'
      +    html+= '    <label for="speed"><small>Speed:</label>'
      +    html+= '    <select id="speed" name="speed">'
      +    html+= string.format('<option value="m_sv_slow"%s>Slow</option>', selected(speed, '1'))
      +    html+= string.format('<option value="m_sv_medium"%s>Medium</option>', selected(speed, '2'))
      +    html+= string.format('<option value="m_sv_fast"%s>Fast</option>', selected(speed, '3'))
      +    html+= '    </select>'
      +    html+= '  </td>'
      +    html+= '</tr><tr></tr></tbody></table>'
      +    html+= '<script>'
      +    html+= 'document.getElementById("speed").addEventListener ("change",function(){la("&"+this.value+"=1");});'
      +    html+= 'document.getElementById("mode").addEventListener ("change",function(){la("&"+this.value+"=1");});'
      +    html+= '</script>'
      +
      +    # Filtration button
      +    html+= '<table style="width:100%"><tbody><tr>'
      +    html+= '  <td style="width:100%">'
      +    html+= '    <button id="bn_filtration" name="bn_filtration" onclick="la(\'&m_sv_filtration=1\');">Filtration</button>'
      +    html+= '  </td>'
      +    html+= '</tr><tr></tr></tbody></table>'
      +
      +    # Light button
      +    html+= '<table style="width:100%"><tbody><tr>'
      +    html+= '  <td style="width:100%">'
      +    html+= '    <button onclick="la(\'&m_sv_light=1\');">Light</button>'
      +    html+= '  </td>'
      +    html+= '</tr><tr></tr></tbody></table>'
      +
      +    # Aux buttons
      +    html+= '<table style="width:100%"><tbody><tr>'
      +    html+= '  <td style="width:25%"><button onclick="la(\'&m_sv_aux=1\');">Aux1</button></td>'
      +    html+= '  <td style="width:25%"><button onclick="la(\'&m_sv_aux=2\');">Aux2</button></td>'
      +    html+= '  <td style="width:25%"><button onclick="la(\'&m_sv_aux=3\');">Aux3</button></td>'
      +    html+= '  <td style="width:25%"><button onclick="la(\'&m_sv_aux=4\');">Aux4</button></td>'
      +    html+= '</tr><tr></tr></tbody></table>'
      +
      +    webserver.content_send(html)
      +    html = nil
      +    tasmota.gc()
      +  end
      +
      +  #- As we can add only one sensor method we will have to combine them besides all other sensor readings in one method -#
      +  def web_sensor()
      +    if webserver.has_arg("m_sv_filtration")
      +      tasmota.cmd("NPFiltration 2")
      +    end
      +
      +    if webserver.has_arg("m_sv_slow")
      +      tasmota.cmd("NPFiltration 1,1")
      +    end
      +    if webserver.has_arg("m_sv_medium")
      +      tasmota.cmd("NPFiltration 1,2")
      +    end
      +    if webserver.has_arg("m_sv_fast")
      +      tasmota.cmd("NPFiltration 1,3")
      +    end
      +
      +    if webserver.has_arg("m_sv_manual")
      +      tasmota.cmd("NPFiltrationmode 0")
      +    end
      +    if webserver.has_arg("m_sv_auto")
      +      tasmota.cmd("NPFiltrationmode 1")
      +    end
      +    if webserver.has_arg("m_sv_heating")
      +      tasmota.cmd("NPFiltrationmode 2")
      +    end
      +    if webserver.has_arg("m_sv_smart")
      +      tasmota.cmd("NPFiltrationmode 3")
      +    end
      +    if webserver.has_arg("m_sv_intelligent")
      +      tasmota.cmd("NPFiltrationmode 4")
      +    end
      +
      +    if webserver.has_arg("m_sv_light")
      +      tasmota.cmd("NPLight 2")
      +    end
      +
      +    if webserver.has_arg("m_sv_aux")
      +      tasmota.cmd("NPAux"+webserver.arg("m_sv_aux")+" TOGGLE")
      +    end
      +  end
      +
      +  def init()
      +  end
      +
      +  def deinit()
      +  end
      +end
      +
      +neopool_driver = NeoPoolButtonMethods()
      +tasmota.add_driver(neopool_driver)
      +

      To activate the new gui elements, go to WebGUI "Consoles" / "Berry Scripting console" and execute

      load("neopoolgui.be")
      +

      ESP32: Make the scripts persistent~

      If you want the extensions to be activated automatically every time you restart your ESP32, save the load() commands into the special file autoexec.be:

      autoexec.be~

      load("neopoolcmd.be")
      +load("neopoolgui.be")
      +
      \ No newline at end of file diff --git a/NodeRed/index.html b/NodeRed/index.html new file mode 100644 index 0000000000..4b6a9f1293 --- /dev/null +++ b/NodeRed/index.html @@ -0,0 +1 @@ + NodeRed - Tasmota

      NodeRed

      Home automation system examples with Pi, Tasmota and Node-Red~

      • Detailed setting up Node-Red and mosquitto on a Raspberry PI and initial control of a Sonoff Switch:

      https://www.instructables.com/id/Powerful-Standalone-Home-Automation-System-Pi-Sono/

      • Detailed setting up of sensors and LEDs using Tasmota:

      https://www.instructables.com/id/Home-Automation-Sonoff-Tasmota-Sensors-LEDs-Develo//

      Credits: @MikePRoberts

      • ESP8266 OTA Firmware Upgrade Manager and Server

      https://flows.nodered.org/flow/888b4cd95250197eb429b2f40d188185

      • Dynamically Populate Dashboard with Buttons to Tasmota-Sonoff Devices

      https://flows.nodered.org/flow/1541bcbba48bb088ec1503dba109f63c

      \ No newline at end of file diff --git a/OTA-over-SCP/index.html b/OTA-over-SCP/index.html new file mode 100644 index 0000000000..370678778c --- /dev/null +++ b/OTA-over-SCP/index.html @@ -0,0 +1,7 @@ + OTA over SCP - Tasmota
      Skip to content

      How to setup and configure "OTA over SCP" upload for PlatformIO. The uploader pushes .bin files to remote OTA server using SCP (SSH connection). Images can be served to Tasmotas from there.

      Configuration~

      To upload .bin images to OTA server using SCP, edit the following lines under target environment:

      ; *** Upload file to OTA server using SCP
      +upload_port = USER@HOST:/path
      +extra_scripts = pio/sftp-uploader.py
      +
      upload_port should be modified to reflect user, host and path on the host where images should be uploaded.

      Requirements~

      SSH communication between the build server and OTA server should be pre-configured so that it doesn't require password (pre-shared keys).

      Add the pre-shared key~

      On a linux client machine type the following to generate the key. Press enter three times (without any input):

      ssh-keygen -t rsa -C "YOUR OWN KEY DESCRIPTION"
      +
      Copy the key to your ssh server. You need to confirm this action. Use your server ssh password (one last time):
      ssh-copy-id -i ~/.ssh/id_rsa.pub USER@HOST
      +
      Optionally, reload the ssh service:
      sudo /etc/init.d/ssh restart
      +

      Upload Tasmota~

      Easy compilation and upload can be performed from the icons at the left side of the PlatformIO screen or use Ctrl + Alt + U to upload (will build if needed).

      \ No newline at end of file diff --git a/Octoprint/index.html b/Octoprint/index.html new file mode 100644 index 0000000000..723a9c96d5 --- /dev/null +++ b/Octoprint/index.html @@ -0,0 +1 @@ + OctoPrint - Tasmota
      Skip to content

      OctoPrint~

      OctoPrint provides a snappy web interface for controlling consumer 3D printers. It is Free Software and released under the GNU Affero General Public License V3 by Gina Häußge.

      Its website can be found at http://www.octoprint.org.

      OctoPrint-Tasmota~

      Jneilliii wrote a plugin to control your Sonoff device with tasmota firmware via OctoPrint, for example shutdown the printer after a print has finished.

      Repository: https://github.com/jneilliii/OctoPrint-Tasmota

      \ No newline at end of file diff --git a/OpenHASP/index.html b/OpenHASP/index.html new file mode 100644 index 0000000000..b0854594fb --- /dev/null +++ b/OpenHASP/index.html @@ -0,0 +1 @@ + OpenHASP - Tasmota
      \ No newline at end of file diff --git a/OpenTherm/index.html b/OpenTherm/index.html new file mode 100644 index 0000000000..347a3bbdd7 --- /dev/null +++ b/OpenTherm/index.html @@ -0,0 +1,80 @@ + OpenTherm - Tasmota
      Skip to content

      OpenTherm

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_OPENTHERM
      +#define USE_OPENTHERM
      +#endif
      +

      Implementation of OpenTherm protocol

      OpenTherm integration is based on OpenTherm protocol specification v2.2 and works with all OpenTherm compatible boilers.

      OpenTherm protocol requires a simple low voltage two-wire connection to the boiler, but voltage levels (7..15V) still much higher than ESP8266 levels, which requires OpenTherm Adapter.

      Compatible OpenTherm Adapters~

      All adapters have a fully isolated circuit from the boiler. However, please be aware that you can damage your boiler. You may also void your boiler warranty by installing this hardware. Please consult with your boiler manufacturer.

      Note

      For my home automation project, I used OpenTherm Gateway Arduino shield; however, this project uses OpenTherm library from Ihor Melnyk. I assume all adapters will work.

      Please note, this integration does not work with the OTGW. You might want to take a look at the HA OpenTherm GW

      Connecting to the Boiler~

      Current integration acts as a master to the boiler.

      Note

      If OpenTherm master device is present, the boiler may disable its front panel controls. Users may not be able to set a boiler and hot water temperature through the boiler. Now it's the responsibility of the master device. This behavior might vary across different boilers.

      You may disable this behavior by removing OpenTherm Slave Status command. However, the integration will not be able to read flame/heating/failure flags.

      OpenTherm Gateway Arduino shield support a gateway mode. As of now, this mode is not supported by the integration.

      As of now, gateway mode is not supported

      Setting Up Tasmota~

      Note

      You need to define USE_OPENTHERM and rebuild Tasmota to enable OpenTherm integration.

      • Log into your Tasmota device
      • Go to Configuration -> Configure Module
      • Select Generic module type, save & restart
      • In the Module parameters window, assign OpenTherm RX and OpenTherm TX to the corresponding pins
      • Save settings and restart

      Note

      The integration attaches ISR to the RX GPIO to avoid pooling. That GPIO should support interrupts. Please consider this while working on the schematics.

      Troubleshooting~

      In order to troubleshoot you may need to enable Debug or More Debug logging level,

      After restart you might see the following in the logs:

      00:00:00 CFG: Loaded from flash at 3F7, Count 72
      +00:00:00 QPC: Flag 0E
      +00:00:00 CFG: CR 338/699
      +00:00:00 SRC: Restart
      +00:00:00 Project tasmota Tasmota Version 8.2.0(sensors)-STAGE
      +00:00:01 [OTH]: perform handshake
      +00:00:01 [OTH]: Processing response. Status=SUCCESS, Response=0x4003011B
      +00:00:01 [OTH]: getLastResponseStatus SUCCESS. Slave Cfg: 4003011B
      +00:00:01 [OTH]: Processing response. Status=SUCCESS, Response=0xC0000000
      +00:00:02 [OTH]: Setting Boiler Temp. Old: 0, New: 85
      +00:00:02 [OTH]: Processing response. Status=SUCCESS, Response=0xD0015500
      +00:00:02 [OTH]: Setting Hot Water Temp. Old: 0, New: 37
      +12:34:58 [OTH]: Processing response. Status=INVALID, Response=0x70730000
      +12:34:58 [OTH]: command OEMD is not supported by the boiler. Last status: INVALID
      +12:35:00 [OTH]: Processing response. Status=INVALID, Response=0xF01B0000
      +12:35:00 [OTH]: command TOUT is not supported by the boiler. Last status: INVALID
      +12:35:00 [OTH]: Processing response. Status=INVALID, Response=0x701C0000
      +12:35:00 [OTH]: command TRET is not supported by the boiler. Last status: INVALID
      +12:35:16 MQT: tele/boiler/SENSOR = {"Time":"2020-05-12T12:35:16","ANALOG":{"A0":7},"OPENTHERM":{"conn":"BUSY","settings":3,"SLAVE":{"FAULT":0,"CH":0,"DHW":0,"FL":0,"COOL":0,"CH2":0,"DIAG":0,"RAW":3221225472},"BTMP":{"FAULT":0,"REQ":85.0,"ACT": 85.0},"HWTMP":{"REQ":37.0,"ACT": 37.0},"ASFF":{"FC":0,"OFC":0},"FLM":0.0,"TB":31.0,"TDHW":30.0,"DHWS":37.0,"TMAX":0.0}}
      +

      In the example, perform handshake was successful, and the OpenTherm integration started to fetch various OpenTherm statuses.

      Note

      Your boiler may not respond to some of the OpenTherm commands. The integration made 3 attempts to execute the command. If the third attempt failed, the command marked as not supported and excluded from the rotation.

      External Thermostat Wiring and Safety~

      Central heating is the last thing you want to fail in your home. You might have significant damage to your property in case of the software bug or your custom hardware or wiring failure.

      As a last resort measure, you probably want to use some sort of mechanical thermostat, which turns on your boiler if the temperature drops below the safe threshold.

      In the worst-case scenario, your OpenTherm hardware stop communicating with the boiler. Proper boiler implementation will flag external panel error and take control back, following the external thermostat circuit state.

      The second issue can be with your thermostat logic, especially if your logic is running on some external device. To address this, OpenTherm integration is using Diagnostics Indication to enable central heating. That way, if your external thermostat report freezing condition, OpenTherm integration activates heating. To enable this option, set the CHOD flag.

      Note

      Another use case for the CHOD flag might be an external hot water tank storage using central heating to heat the hot water in the tank.

      Configuration~

      OpenTherm integration supports the following commands.

      • ot_flags Get/Set OpenTherm flags
      • ot_tboiler Get/Set central heating temperature
      • ot_twater Get/Set domestic hot water temperature
      • ot_save_setpoints Save central heating and domestic hot water temperatures
      • ot_ch Activate/Deactivate central heating

      ot_flags command~

      OpenTherm integration supports the following flags:

      • CHOD - Enable CH (central heating) on diagnostics flag. See External Thermostat section
      • DHW - Enable Domestic Hot Water. If you have an on-demand gas heater, it won't start heating immediately, but enable heating when water is on
      • CH - If set, activate central heating permanently, following the ot_tboiler setpoint value. If disabled and CHOD is set, it follows the Diagnostics flag. Otherwise, heating is controlled by the ot_ch status
      • COOL - Enable cooling, if supported. Refer to your boiler manual.
      • OTC - Enable external temperature compensation thermistor. Refer to your boiler manual.
      • CH2 - Enable auxiliary central heating. Refer to your boiler manual.

      Note

      During the first run, ot_flags is set to the CHOD,DHW. Hot water setpoint set to 36-degree Celsius and central heating temperature set to 85 degree celsius

      To get OpenTherm flags, type ot_flags

      13:49:48 CMD: ot_flags
      +13:49:48 MQT: stat/boiler/RESULT = CHOD,DHW
      +

      To set OpenTherm flags, type ot_flags DHW,CH,OTC

      13:49:48 CMD: ot_flags DHW,CH,OTC
      +13:49:48 MQT: stat/boiler/RESULT = DHW,CH,OTC
      +

      ot_tboiler command~

      ot_tboiler set boiler (CH) desired temperature. The actual command to the boiler will be issued if your new temperature difference bigger than the OPENTHERM_BOILER_SETPOINT_TOLERANCE value which is 1-degree Celsius as of today.

      14:12:04 CMD: ot_tboiler
      +14:12:04 MQT: stat/boiler/RESULT = {"ot_tboiler":60.0}
      +
      14:12:55 CMD: ot_tboiler 85
      +14:12:55 MQT: stat/boiler/RESULT = {"ot_tboiler":85.0}
      +14:12:57 [OTH]: Setting Boiler Temp. Old: 60, New: 85
      +

      Note

      Some boilers might write setpoint temperature into the Flash memory. Having PID controlled appliance may produce a lot of small fluctuations in the setpoint value, wearing out Boiler flash memory.

      Warning

      ot_tboiler do not write the value in the Tasmota settings, reducing Flash memory writes. To store it permanently, invoke ot_save_setpoints command after ot_tboiler command

      ot_twater command~

      ot_twater set domestic hot water temperature setpoint (DHW).

      Warning

      ot_twater do not write the value in the Tasmota settings. To store it permanently, invoke ot_save_setpoints command after ot_twater command.

      14:13:55 CMD: ot_twater
      +14:13:55 MQT: stat/boiler/RESULT = {"ot_twater":38.0}
      +
      14:13:55 CMD: ot_twater 40
      +14:13:55 MQT: stat/boiler/RESULT = {"ot_twater":40.0}
      +

      ot_save_setpoints command~

      ot_save_setpoints store boiler and domestic hot water setpoints into the flash memory of the Tasmota.

      ot_ch command~

      ot_ch enable or disable central heating (CH). The Boiler follows the ot_tboiler temperature setpoint.

      14:14:57 CMD: ot_ch
      +14:14:57 MQT: stat/boiler/RESULT = {"ot_ch":0}
      +
      14:15:57 CMD: ot_ch 1
      +14:15:57 MQT: stat/boiler/RESULT = {"ot_ch":1}
      +

      Warning

      ot_ch 0 won't turn the boiler off, if CHOD flag is set and external thermostat requires heat

      Setting Hot Water temperature~

      One of the drawbacks, at least with my boiler, is that the boiler disables all the external knobs, so I can not set up the desired hot water temperature from the boiler itself. You might come up with the automation, setting hot water temperature by some schedule.

      Another way is to use Home Assistant with the HomeKit integration. I created MQTT Hot Water Heater integration to the Home Assistant, so the Water Heater is exposed to the Home Kit. You should be able to install it through HACS

      You can add the following configuration into the configuration.yaml of your Home Assistant set up.

      water_heater:
      +  platform: mqtt_water_heater
      +  name: "Water Heater"
      +  state_topic: "tele/boiler/SENSOR"
      +  command_topic: "cmnd/boiler/OT_TWATER"
      +  value_template: "{{ value_json.OPENTHERM.DHWS }}"
      +  qos: 0
      +  availability_topic: "tele/boiler/LWT"
      +  payload_available: "Online"
      +  payload_not_available: "Offline"
      +  target_temperature: 38
      +  heater_min_temperature: 35
      +  heater_max_temperature: 50
      +

      Automation Examples~

      This automation example turns on then Bathroom towel heater if someone is using hot water more than 5 minutes in the evening or more than 7 minutes in the evening. Bathroom towel heater has a coolant pump relay at 192.168.1.xx Also, it always turns on the heater on Clock#Timer3 event, working according to the schedule

      It publishes state to the st/boiler/rr_dry for the Home Assistant bookkeeping. Also, it set boiler temperature to the %var3% value if external heating is required during the bathroom Dry mode.

      Also, it syncs up the coolant pump state in case of the pump controller power outage.

      # var1 - hot water ON time to enable bathroom Dry mode
      +# var2 - Bathroom Dry Mode boiler setpoint
      +# var3 - Normal Mode boiler setpoint
      +# var15 - a lock of the flame mode to avoid timer restart
      +# var16 - sync power4 state
      +# use VAR3 to set the actual boiler temperature setpoint
      +
      +# If heat demand is on during RR Dry Mode, the temperature should be set back to the normal
      +
      +Rule1
      +on system#boot do backlog TelePeriod 20; var1=540; var2=60; var3=85; var15 0; var16 0 endon
      +on tele-OPENTHERM#SLAVE#FL do WebSend [192.168.1.xx] POWER4 %var16%; endon
      +on Clock#Timer=1 do var1=300 endon
      +on Clock#Timer=2 do var1=420 endon
      +on Clock#Timer=3 do event dr=1 endon
      +on var3#state do ot_tboiler %value% endon
      +
      +Rule2
      +on tele-OPENTHERM#SLAVE#DHW>%var15% do backlog RuleTimer1 %var1%; var15 1 endon
      +on tele-OPENTHERM#SLAVE#DHW=0 do backlog RuleTimer1 0; var15 0 endon
      +on Rules#Timer=2 do backlog ot_ch 0; ot_tboiler %var3%; var16 0; WebSend [192.168.1.xx] POWER4 OFF; publish st/boiler/rr_dry 0 endon
      +on Rules#Timer=1 do event dr=1 endon
      +on event#dr do backlog RuleTimer2 7200; ot_ch 1; ot_tboiler %var2%; var16 1; WebSend [192.168.1.xx] POWER4 ON; publish st/boiler/rr_dry 1 endon
      +
      +Rule3
      +on tele-OPENTHERM#SLAVE#DIAG=1 do ot_tboiler %var3%; endon
      +on tele-OPENTHERM#SLAVE#DIAG=0 do ot_tboiler %var2%; endon
      +
      \ No newline at end of file diff --git a/P1-Smart-Meter/index.html b/P1-Smart-Meter/index.html new file mode 100644 index 0000000000..986770535c --- /dev/null +++ b/P1-Smart-Meter/index.html @@ -0,0 +1,54 @@ + P1 Smart Meter - Tasmota
      Skip to content

      P1 Smart Meter~

      Reading serial data from a P1 smart meter using a Wemos with Tasmota installed.

      Tested on the following smart meters:

      • Kaifa MA105C
      • De Landis + Gyr, E350 (ZCF110)
      • Landis + Gyr, E360
      • Sanxing SX631 (S34U18)

      Schematics~

      The transistor makes sure that the RxD signal is converted and inverted to 3.3V. According to the DSMR v5.0.2 P1 specification the P1 connector on the meter provides 5V DC output for the OSM (Other Service Module) connected to this port, which is able to continuously supply maximum current of 250mA. A Wemos D1 mini module draws way less than 100mA so it is perfectly safe to use this as a power source. It can be powered through the 5V pin just inserting a protection diode.

      Tasmota Settings~

      In the Configuration -> Configure Module page, select module Generic (18)

      From the web console set the serial delimiter to 10 (newline). This makes Tasmota publish each line of the telegram separately to mqtt.

      • SerialDelimiter 10
      • SerialSend 1

      For more details see serial-bridge.

      Should you run into problems with serial buffer overflows, then try to increase the serial buffer size using SerialBuffer 520. This will not be automatically persisted, so try the command first and when the device works fine after having increased the serial buffer size, then try to add a rule like:

      Rule1 ON Power1#Boot DO SerialBuffer 520 ENDON
      +

      Example output~

      Below an example of the telegram message published (per line) to mqtt. From here your HA system can process the data required for your needs.

      09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"/KFM5KAIFA-METER\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-3:0.2.8(42)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"0-0:1.0.0(200913101618S)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"0-0:96.1.1(4530303235303030303639363432393136)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:1.8.1(005779.835*kWh)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:1.8.2(005583.617*kWh)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:2.8.1(000000.000*kWh)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:2.8.2(000000.000*kWh)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"0-0:96.14.0(0001)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:1.7.0(00.498*kW)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:2.7.0(00.000*kW)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"0-0:96.7.21(00000)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"0-0:96.7.9(00000)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:99.97.0(1)(0-0:96.7.19)(000101000001W)(2147483647*s)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:32.32.0(00000)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:32.36.0(00000)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"0-0:96.13.1()\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"0-0:96.13.0()\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:31.7.0(002*A)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:21.7.0(00.496*kW)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"1-0:22.7.0(00.000*kW)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"0-1:24.1.0(003)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"0-1:96.1.0(4730303332353631323736373836373136)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"0-1:24.2.1(200913100000S)(04139.079*m3)\r"}
      +09:16:17 MQT: tele/wemos-9/RESULT = {"SerialReceived":"!F798\r"}
      +

      Description of each line~

      see also DSMR 5.0 - P1 Companion Standard

      Header information - {"SerialReceived":"/KFM5KAIFA-METER"}
      +Empty line - {"SerialReceived":""}
      +Version information for P1 output - {"SerialReceived":"1-3:0.2.8(42)"}
      +Date-time stamp of the P1 message - {"SerialReceived":"0-0:1.0.0(181227093413W)"}
      +Equipment identifier - {"SerialReceived":"0-0:96.1.1(4530303235303030303639363432393136)"}
      +electricityUsedTariff1 >> Meter Reading electricity delivered to client (Tariff 1) in 0,001 kWh - {"SerialReceived":"1-0:1.8.1(002293.192*kWh)"}
      +electricityUsedTariff2 >> Meter Reading electricity delivered to client (Tariff 2) in 0,001 kWh - {"SerialReceived":"1-0:1.8.2(002523.640*kWh)"}
      +Meter Reading electricity delivered by client (Tariff 1) in 0,001 kWh - {"SerialReceived":"1-0:2.8.1(000000.000*kWh)"}
      +Meter Reading electricity delivered by client (Tariff 2) in 0,001 kWh - {"SerialReceived":"1-0:2.8.2(000000.000*kWh)"}
      +electricityActiveTariff >> Tariff indicator electricity. The tariff indicator can also be used to switch tariff dependent loads e.g boilers. This is the responsibility of the P1 user - {"SerialReceived":"0-0:96.14.0(0002)"}
      +Actual electricity power delivered (+P) in 1 Watt resolution - {"SerialReceived":"1-0:1.7.0(00.474*kW)"}
      +Actual electricity power received (-P) in 1 Watt resolution - {"SerialReceived":"1-0:2.7.0(00.000*kW)"}
      +Number of power failures in any phase - {"SerialReceived":"0-0:96.7.21(00000)"}
      +Number of long power failures in any phase - {"SerialReceived":"0-0:96.7.9(00000)"}
      +Power Failure Event Log (long power failures) - {"SerialReceived":"1-0:99.97.0(1)(0-0:96.7.19)(000101000001W)(2147483647*s)"}
      +Number of voltage sags in phase L1 - {"SerialReceived":"1-0:32.32.0(00000)"}
      +Number of voltage swells in phase L1 - {"SerialReceived":"1-0:32.36.0(00000)"}
      +Text message max 1024 characters. - {"SerialReceived":"0-0:96.13.1()"}
      +Text message max 1024 characters. - {"SerialReceived":"0-0:96.13.0()"}
      +Instantaneous current L1 in A resolution - {"SerialReceived":"1-0:31.7.0(002*A)"}
      +Instantaneous active power L1 (+P) in W resolution - {"SerialReceived":"1-0:21.7.0(00.474*kW)"}
      +Instantaneous active power L1 (-P) in W resolution - {"SerialReceived":"1-0:22.7.0(00.000*kW)"}
      +Device-Type - {"SerialReceived":"0-1:24.1.0(003)"}
      +Equipment identifier (Gas) - {"SerialReceived":"0-1:96.1.0(4730303332353631323736373836373136)"}
      +GasMeterReadingFiveMinutes >> Last 5-minute value (temperature converted), gas delivered to client in m3, including decimal values and capture time - {"SerialReceived":"0-1:24.2.1(181227090000W)(02910.491*m3)"}
      +{"SerialReceived":"!5E3E"}
      +
      Also see Tasmota's Smart Meter Interface if you want to have these OBIS lines translated in selected sensors populatinng MQTT payloads.

      Additional info * Kaifa Meters (Dutch) * DSMR 5.0 - P1 Companion Standard

      \ No newline at end of file diff --git a/PAJ7620/index.html b/PAJ7620/index.html new file mode 100644 index 0000000000..868f89b6ee --- /dev/null +++ b/PAJ7620/index.html @@ -0,0 +1,4 @@ + PAJ7620U2 gesture sensor - Tasmota
      Skip to content

      PAJ7620U2 gesture sensor~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_PAJ7620
      +#define USE_PAJ7620         // [I2cDriver34] Enable PAJ7620 gesture sensor (I2C address 0x73) (+2.5k code)
      +#endif
      +

      PAJ7620U2 is an integrated gesture recognition I2C sensor from PixArt-Imaging Inc. based on infrared. It also has built-in proximity detection and can sense various properties like position (x,y,z) and speed.

      Gesture recognition seems to be more stable than with the APDS-9960, which on the other hand is a lot cheaper.

      Configuration~

      Wiring~

      Breakout ESP
      VCC/VIN +3.3VDC
      GND GND
      SCL GPIOy
      SDA GPIOx
      INT Not used

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. GPIOx to I2C SDA
      2. GPIOy to I2C SCL

      After a reboot the driver will detect the PAJ7620 automatically.

      After restart Tasmota needs some time to completely configure its state. In this time frame it is likely to miss some gestures. This should stabilize after a few moments.

      Commands~

      To use the sensor you need to switch to the desired mode of operation with Sensor50 <x> where <x> = 0…5. It will not appear in the webUI but it can be observed via MQTT messages in console.

      0 - Off~

      Sensor muted, no readings in Tasmota.

      1 - Gesture Mode~

      Reports gesture movement with:
      Up
      Down
      Left
      Right
      Near
      Far
      CW (clockwise rotation)
      CCW (counter-clockwise rotation)

      As expected, "Near" and "Far" gestures are tricky and you have to train your movements to catch them. Sometimes the sensor reports "Near" and "Far" at once (which will be discarded).
      There is some postprocessing to allow the object (hand or finger) to move into the sensing area and delay the initial direction report (up, down, left, right) to give the chance to trigger (the intended) "Near" or "Far" movement. Especially "Far" is a bit harder to achieve.

      example:
      …{Up:1} = up gesture once
      …{Left:3} = left gesture 3 times in a row, without any other gesture in between

      2 - Proximity Mode~

      Arbitrary values between 0 (far away) and 255 (very near) are given. Exit from the sensor field will always give at least one "zero message". tele is only triggered, when the value has changed.

      example:
      …{Proximity:255} = close proximity, almost touching the sensor
      …{Proximity:0} = object has left the sensing area

      3 - Corner Mode~

      Sensing area is organised in quarters. An object in one of the corners will trigger the corresponding number.

      1 2
      3 4

      example:
      …{Corner:2} = object in upper right corner

      4 - PIN Mode:~

      A fluent movement of an object through a given sequence of corners (similar to unlocking a smartphone) will trigger a valid "PIN". The next corner must be reached in about 0.7 seconds.

      example:
      …{PIN:1} = valid PIN

      5 - Cursor Mode:~

      Shows x- and y-coordinates. Mainly intended for debugging and "seeing" the sensing area. This reads only the upper 5-bit-values, which automatically removes much of the jitter, giving values between 0 and 15.

      example:
      …{x:1, y:15} = upper left corner

      The sensor provides some more goodies, like velocity of an object, so if someone has a fancy use case for this, feel free to open a feature request. Of course it would be possible to mix the modes, but this can produce a lot of MQTT-messages. This could be added later upon user request (based on real world use cases).

      Breakout boards~

      image image image

      \ No newline at end of file diff --git a/PCA9557/index.html b/PCA9557/index.html new file mode 100644 index 0000000000..1cbaf80cf2 --- /dev/null +++ b/PCA9557/index.html @@ -0,0 +1,17 @@ + PCA9557 GPIO Expander - Tasmota
      Skip to content

      PCA9557 GPIO Expander~

      Technical Data from the manufacturer: * NXP PCA9557

      The PCA9557 has 8 IO pins which the PCA9557 driver uses as D0 - D7. This is visualized in the circuit diagram below.

      Manual Wiring for PCA9557

      You will need to pick an I2C address using the address mapping according to pin A0, A1, and A2 as from the datasheet as follows:

      PCA9557 I2C Address Map

      Supporting modes~

      Starting with Tasmota v12.5.0.x several and mixed PCA9557 are supported, adding switches, buttons and relays acted on as if they were directly connected to the ESP8266 or ESP32 configured using a JSON file containing a template describing the GPIO's as used on the basic Tasmota device.

      To enable it you will only need to add in user_config_override.h

      #define USE_PCA9557

      This enables the driver which in turn at restart will search for the JSON file in three possible locations:

      • if a filesystem is present it looks for file pca9557.dat
      • if not found and rules are supported it looks for a specific rule entry like on file#pca9557.dat do <template> endon
      • if not found and scripts are supported it looks for a specific script like -y <template>

      If no JSON file is found the driver does not claim any PCA9557 device.

      A typical JSON template would look like {"NAME":"PCA9557 expander","BASE":0,"GPIO":[224,225,226,227,32,33,34,35]} which adds four relays and four buttons.

      The template consists of a "NAME" data pair with any description of the template, an optional "BASE" data pair selecting if either relative (0 = default) or absolute (1) button and/or switch numbering is used and a "GPIO" data pair with numbers representing the functions of the GPIO's in order from lowest I2C address IO0 to highest I2C address IO7 and are based on the numbers known from the base tasmota template used on the ESP8266 or ESP32.

      The following list contains the current supported functions:

      Function Code Description
      None 0 Not used
      Button_n1..32 Bn 64..95 Button to Gnd (needs external resistor)
      Button_in1..32 Bin 128..159 Button inverted to Vcc (needs external resistor)
      Switch_n1..28 Sn 192..219 Switch to Gnd (needs external resistor)
      Relay1..32 R 224..255 Relay
      Relay_i1..32 Ri 256..287 Relay inverted
      Output_Hi Oh 3840 Fixed output high
      Output_lo Ol 3872 Fixed output low

      Some example templates

                                S3  S2  B2 B3 Oh   B1 S1    R1        R4  R2  R3  S4
      +{"NAME":"PCA9557","GPIO":[194,193,65,66,3840,64,192,0,224,0,0,0,227,225,226,195]}
      +
      +Inverted relays and buttons               Ri1 Ri2 Ri3 Ri4 Ri5 Ri6 Ri7 Ri8 B1 B2 B3 B4 B5 B6 B7 B8
      +{"NAME":"PCA9557 A=Ri1-8, B=B1-8","GPIO":[256,257,258,259,260,261,262,263,32,33,34,35,36,37,38,39]}
      +
      +Unique inverted relays and buttons with offset 2     Ri3 Ri4 Ri5 Ri6 Ri7 Ri8 Ri9 Ri10B3 B4 B5 B6 B7 B8 B9 B10
      +{"NAME":"PCA9557 A=Ri2-10, B=B2-10","BASE":1,"GPIO":[258,259,260,261,262,263,264,265,34,35,36,37,38,39,40,41]}
      +
      +Buttons, relays, buttons and relays                        B1 B2 B3 B4 B5 B6 B7 B8 R1  R2  R3  R4  R5  R6  R7  R8  B9 B10B11B12B13B14B15B16R9  R10 R11 R12 R13 R14 R15 R16
      +{"NAME":"PCA9557 A=B1-8, B=R1-8, C=B9-16, D=R9-16","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231,40,41,42,43,44,45,46,47,232,233,234,235,236,237,238,239]}
      +

      Since the PCA9557 has no interrupt pin, buttons and switches will be polled every 50ms.

      You will need to define the address you are using in user_config_override.h for the driver to know on which address the PCA9557 is expected to be found.

      #define USE_PCA9557_ADDR 0x18

      The PCA9557 chips allow for both INPUT and OUTPUT.

      If OUTPUT is enabled, telemetry data for the current state of OUTPUT pins will also be provided by telemetry.

      Usage of the driver~

      The PCA9557 chip (or breakout board) must be connected to the ESP8266/ESP32 and the I2C pins must be configured for the module similar to the following:

      Tasmota Generic_Config_Menu

      Once that is complete you may want to confirm that the Tasmota firmware is finding your PCA9557 chip by sending the command through serial or MQTT:
      I2Cscan

      You should see a response giving you an address within the range of the PCA9557 chip (0x18 through 0x1F) which may look as follows
      MQT: stat/tasmota/RESULT = {"I2CScan":"Device(s) found at 0x18"}

      If the extender is not detected, check your wiring and pin configuration.

      If sucessful, you should be able to see the changes in Tasmota main web page. Following example has 8 IO lines defined as relays:

      Tasmota device main web page

      Configuration example~

      You can add all necessary settings at once in your user_config_override.h. The following example adds 8 relays (commanded with POWER1 to POWER8), PCA9557 has all address bits tied to GND (0x18) while ESP8266 GPIO 0 and 2 are used for I2C SDA and SCL.

      #define USE_PCA9557
      +#define USE_PCA9557_ADDR 0x18
      +
      +#define USER_TEMPLATE "{\"NAME\":\"Lights\",\"GPIO\":[608,0,640,0,0,0,0,0,0,0,0,0,0,0],\"FLAG\":0,\"BASE\":18}"
      +#define USER_RULE1 "On file#pca9557.dat DO {\"NAME\":\"Lights\",\"BASE\":0,\"GPIO\":[256,257,258,259,260,261,262,263]} ENDON"
      +

      \ No newline at end of file diff --git a/PCA9685/index.html b/PCA9685/index.html new file mode 100644 index 0000000000..78ac63daa5 --- /dev/null +++ b/PCA9685/index.html @@ -0,0 +1,12 @@ + PCA9685 12-bit PWM controller - Tasmota
      Skip to content

      PCA9685 12-bit PWM controller~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_PCA9685
      +#define USE_PCA9685                     // [I2cDriver1] Enable PCA9685 I2C HW PWM Driver - Must define I2C Address in #define USE_PCA9685_ADDR below - range 0x40 - 0x47 (+1k4 code)
      +  #define USE_PCA9685_ADDR 0x40         // Enable PCA9685 I2C Address to use (Must be within range 0x40 through 0x47 - set according to your wired setup)
      +  #define USE_PCA9685_FREQ 50           // Define default PWM frequency in Hz to be used (must be within 24 to 1526) - If other value is used, it will revert to 50Hz
      +#endif
      +

      Technical Data: Product Information from NXP

      IMPLEMENTATION STATUS IN TASMOTA~

      The PCA9685 driver is implemented in such a way that it may be used as standard individual OUTPUT pins, or as PWM capable OUTPUT pins - The latter offloads the PWM functionality from the ESP8266 insofar that the PCA9685 will continue to perform its configured output PWM/ON/OFF state without direct control or intervention from the Tasmota firmware.

      Support was added in line with the datasheet specification insofar that the PWM frequency can be set from 24hz all the way up to 1526hz.

      This driver does not currently have any perpetual settings so will revert to a base frequency of 50hz (usually suitable for most applications) and will be set in an OFF state for all pins during power-up and/or reset.

      The latter may change as the driver grows in cases where user requirements and development requirements are met.

      USAGE OF THE PCA9685 DRIVER IN TASMOTA~

      The driver needs to be connected to the I2C bus of your Tasmota powered device (note that most Sonoff devices will not have reachable I2C pins so it's applicable to a limited number, so most likely only applicable to bare ESP8266 or WeMos type users.

      If you modify a Sonoff or similar device to gain access to the I2C bus please be aware that other problems may persist and that for such use cases support in Tasmota chat or Issues is not guaranteed in any way whatsoever)

      For information on how to set up a development environment please check the wiki on PlatformIO

      Note that the I2C selection must correspond with how you have wired the module or chip as incorrect addressing will result in the PCA9685 not being detected. The valid I2C address range is 0x40 through 0x47 for the PCA9685 and most off-the-shelf modules would likely default to 0x40.

      If you are unsure please use I2CScan from Tasmota console to scan for devices on the I2C bus and you should find a device within the mentioned range.

      You may also get a discovery on 0x70 but please do not use this address as it is a broadcast address and the driver does not currently support its implementation.

      Also be aware of other I2C devices you have connected to the same Tasmota driven device in order to avoid I2C address conflicts.

      Once connected, correctly flashed, and configured Tasmota will detect the device automatically on startup.

      DRIVER USAGE~

      There is no web interface for this driver. The driver is used via MQTT or console command using the following available commands and their respective parameters

      driver15 pwmf,frequency   // where frequency is the PWM frequency from 24 to 1526 in Hz
      +driver15 pwm,pin,pwmvalue // where pin = LED pin 0 through 15 and pwmvalue is the pulse width between 0 and 4096
      +driver15 pwm,pin,ON       // Fully turn a specific pin/LED ON
      +driver15 pwm,pin,OFF      // Fully turn a specific pin/LED OFF
      +driver15 reset            // Reset to power-up settings - i.e. F=50hz and all pins in OFF state
      +driver15 status           // Will return a JSON string containing all the current settings / parameters
      +

      OTHER IMPORTANT INFORMATION~

      • Please remember to consider the voltage and current limitations of the chip and per pin output current limitations as outlined in the datasheet.
      • You may also get a discovery on 0x70 but please do not use this address as it is a broadcast address and the driver does not currently support its implementation.
      • Also be aware of other I2C devices you have connected to the same Tasmota driven device in order to avoid I2C address conflicts.

      OUTSTANDING FEATURE REQUESTS~

      • Dimming ON / OFF a dimming value in a certain time and fade on "ON / OFF" (Not yet scheduled for implementation)
      • Allow usage of RGBW in pairs of 4 PWM outputs (i.e. drive 4 x 4pin RGBW LED's) (Not yet scheduled for implementation)
      \ No newline at end of file diff --git a/PCF8574/index.html b/PCF8574/index.html new file mode 100644 index 0000000000..f71c8a7544 --- /dev/null +++ b/PCF8574/index.html @@ -0,0 +1,20 @@ + PCF8574 / PCF8574A GPIO Expander - Tasmota
      Skip to content

      PCF8574 / PCF8574A GPIO Expander~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #define USE_I2C                                // Add support for I2C
      +#define USE_PCF8574                            // [I2cDriver2] Enable PCF8574 I/O Expander (I2C addresses 0x20 - 0x26 and 0x39 - 0x3F) (+2k1 code)
      +//  #define USE_PCF8574_MODE2                    // Enable Mode2 virtual relays/buttons/switches (+2k3 code)
      +//  #define USE_PCF8574_SENSOR                   // Enable Mode1 inputs and outputs in SENSOR message (+0k2 code)
      +//  #define USE_PCF8574_DISPLAYINPUT             // Enable Mode1 inputs display in Web page (+0k2 code)
      +//  #define USE_PCF8574_MQTTINPUT                // Enable Mode1 MQTT message & rule process on input change detection : stat/%topic%/PCF8574_INP = {"Time":"2021-03-07T16:19:23+01:00","PCF8574-1_INP":{"D1":1}} (+0k5 code)
      +
      In order to use PCF8574 mode 2, uncomment USE_PCF8574_MODE2. In order to use inputs, uncomment the last three lines.

      Introduction~

      PCF8574 and PCF8574A are I2C 8-bit IO port extender originally designed by Philips (now NXP) but also now available from various manufacturer.

      As usual when using an electronic part, reading the datasheet is highly recommended as the below document only focus on Tasmota integration.

      A few different breakout boards are available although some are dedicated to be mounted as a backpack on standard 16x2 or 16x4 LCD displays and are not suitable for general I/Os (but works well with tasmota-display).

      PCF8574 breakout modules

      • On the left: generic modules suitable for extending IO
      • On the right: specific module to control a LCD display (not the purpose of this doc page)

      Supported I2C addresses and number of PCF8574~

      PCF8574 and PCF8574A are identical functionally and each can be configured to work on 1 of 8 possible I2C address. PCF8574 can use one address of 0x20 to 0x27 and PCF8574A can use one of 0x38 to 0x3F.

      As of today, Tasmota driver supports:

      • Up to 4 PCF8574 OR PCF8574A is supported by Tasmota allowing up to 32 additional GPIO pins.
      • Addresses 0x27 and 0x38 are excluded to avoid conflict with other I2C peripheral which can't be differentiated at run-time.

      If USE_MCP230xx_ADDR is defined, this address is reserved for MCP230XX IO expander.

      The first 2 lines are mandatory to enable I2C support and including the driver in the build. The 3 other lines allows to add optional features to support inputs. By default only the "outputs" feature is enabled.

      Tasmota Configuration~

      Note

      Once the firmware with the PCF8574 driver has been loaded, make sure to have it enabled with I2Cdriver2 1.

      Starting with Tasmota v12.4.0.2 there are two different modes to use PCF8574(A):

      • The original approach (now called Mode 1) supports user configurable features using a GUI menu.
      • The latest approach called Mode 2, supports adding switches, buttons and relays configured using a JSON file containing a template describing the GPIO's as used on the basic Tasmota device.

      Mode 2~

      To enable Mode 2 you will need to add in user_config_override.h

      #define USE_PCF8574_MODE2

      This enables the driver which will at restart search for the JSON file in three possible locations:

      • if a filesystem is present it looks for file pcf8574.dat
      • if not found and rules are supported it looks for a specific rule entry like on file#pcf8574.dat do <template> endon
      • if not found and scripts are supported it looks for a specific script like -y <template>

      Note

      If no JSON file is found the driver continues in mode 1.

      A typical JSON template would look like {"NAME":"PCF8574 expander","BASE":0,"GPIO":[224,225,226,227,32,33,34,35]} which adds four relays and four buttons.

      The template consists of a "NAME" data pair with any description of the template, an optional "BASE" data pair selecting if either relative (0 = default) or absolute (1) button and/or switch numbering is used and a "GPIO" data pair with numbers representing the functions of the GPIO's in order from lowest I2C address D0 to highest I2C address D7 and are based on the numbers known from the base tasmota template used on the ESP8266 or ESP32.

      The following list contains the current supported functions:

      Function Code Description
      None 0 Not used
      Button1..32 B 32..63 Button to Gnd with internal pullup
      Button_n1..32 Bn 64..95 Button to Gnd without internal pullup
      Button_i1..32 Bi 96..127 Button inverted to Vcc with internal pullup
      Button_in1..32 Bin 128..159 Button inverted to Vcc without internal pullup
      Switch1..28 S 160..187 Switch to Gnd with internal pullup
      Switch_n1..28 Sn 192..219 Switch to Gnd without internal pullup
      Relay1..32 R 224..255 Relay
      Relay_i1..32 Ri 256..287 Relay inverted
      Output_Hi Oh 3840 Fixed output high
      Output_lo Ol 3872 Fixed output low

      Some example templates

      Unique inverted relays and buttons with offset 2                           B3 B4 B5 B6 Ri3 Ri4 Ri5 Ri6 B7 Ri7 B8 Ri8 B9 Ri9 B10Ri10
      +{"NAME":"PCF8574 A=B3456,Ri3456,B=B7Ri7B8Ri8B9Ri9B10Ri10","BASE":1,"GPIO":[34,35,36,37,258,259,260,261,38,262,39,263,40,264,41,265]}
      +
      +Inverted relays and buttons               Ri8 Ri7 Ri6 Ri5 Ri4 Ri3 Ri2 Ri1 B1 B2 B3 B4 B5 B6 B7 B8
      +{"NAME":"PCF8574 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]}
      +
      +                                                     B1 B2 B3 B4 Ri4 Ri3 Ri2 Ri1 B5 B6 B7 B8 Ri8 Ri7 Ri6 Ri5
      +{"NAME":"PCF8574 A=B1-4,Ri4-1, B=B5-8,Ri8-5","GPIO":[32,33,34,35,259,258,257,256,36,37,38,39,263,262,261,260]}
      +

      Mode 1~

      PCF8574 can be configured from Tasmota web GUI in "Configure" => "Configure PCF8574" PCF8574 configuration screen

      Each IO port can be configured as Input or Output in a similar way as a native GPIO of the ESP.

      If you are using outputs to drive relays, it is possible to choose if the relay is activated by a HIGH signal (checkbox "Invert Ports" unchecked) or a LOW signal (checkbox checked). The selection applies to all output ports. This checkbox can also be controlled by SetOption81.

      Once configuration is complete, it must be saved by clicking on the green "Save" button. Like for general ESP GPIO configuration, this will trigger a reboot of the ESP.

      It is not possible to change pin definition at run-time.

      Outputs~

      A PCF8574 pin configured as an output support all features of a Tasmota Relay component.

      It is assigned a Power index and can be controlled by Power command (on, off, toggle). Power indexes of PCF8574 outputs are assigned after the ESP GPIO configured as Relay. For example, if you have Relay 1 (Power1) to Relay 4 (Power4) configured on the ESP's GPIO, PCF8574 outputs will start at Power5.

      A state text and an on/off button are automatically created on the Web GUI and syncs with the pin state.

      All Power features are supported including PowerOnState, PulseTime, Blink, SetOption0, ...

      PCF8574 power GUI

      Usage~

      Enabling USE_PCF8574_SENSOR adds a PCF8574-xx field into the JSON payload of the tele/topic/SENSOR message. The form of the message is:

      {"Time":"2021-03-11T19:50:58+01:00","PCF8574-1":{"D0":1,"D1":1,"D2":1,"D3":1,"D4":0,"D5":0,"D6":0,"D7":0}}
      +

      As you can see, all pins are listed, including both inputs and outputs. The value reported is the digital level of the pin. If "Invert Ports" has been enabled, Power ON will be reported as 0 as the pin is at a LOW level.

      As for any sensor published in the tele/topic/SENSOR message, it is possible to use Rules triggers such as:

      ON tele-PCF8574-1#D0 DO something_with %value% ENDON
      +
      Numerical operators such as == can be used to compare to 0 or 1. See also change detection.

      PCF8574 inputs pins in the Web GUI~

      Enabling USE_PCF8574_DISPLAYINPUT will add the state of PCF8574 inputs displayed as sensors in the Web GUI. Outputs are not represented here as they are already shown as Power.

      PCF8574 inputs GUI

      Value of pin is updated in almost "real-time".

      Input Change Detection~

      While reporting the pin level in SENSOR or on the GUI is interesting, it is even better to detect pin change. This is enabled by USE_PCF8574_MQTTINPUT. When this feature is enabled at build time, a test will be performed every 50ms to detect if an input pin has changed. In that case, Tasmota will publish on stat/topic/PCF8574_INP a JSON payload with the PCF8574 index and the pin level:

      20:19:39.385 MQT: stat/topic/PCF8574_INP = {"Time":"2021-03-11T20:19:39+01:00","PCF8574-1_INP":{"D0":0}}
      +20:19:39.584 MQT: stat/topic/PCF8574_INP = {"Time":"2021-03-11T20:19:39+01:00","PCF8574-1_INP":{"D0":1}}
      +

      This can be caught in rules such as:

      Implementing a Power push "Button":

      ON PCF8574-1_INP#D0=0 DO Power2 toggle ENDON
      +

      \ No newline at end of file diff --git a/PID-Control/index.html b/PID-Control/index.html new file mode 100644 index 0000000000..8951ea49d2 --- /dev/null +++ b/PID-Control/index.html @@ -0,0 +1 @@ + PID Control - Tasmota

      PID Control

      This extension adds a PID (Proportional Integral Derivative) feature into the Tasmota software.

      The PID algorithm is designed to be used to control real-world processes. This includes room heating/cooling, temperature control when brewing, and a multitude of other processes. The PID tuning parameters are designed to be meaningful in the real world (rather than the abstract Ki Kd Kp that are often used which are completely meaningless to most). The algorithm is based on that in the node-red node node-red-contrib-pid which has been well received.

      In use it can either regularly be given the current process value via MQTT or if the device has a sensor attached then that sensor can be used to read the process value. So using any Tasmota-capable device with e.g. a temperature sensor (e.g. a TH10 with a DS18B20) the complete PID loop control can be built into the device so that the process will continue to be controlled even if the wifi is down. This is a very cost effective way of achieving PID control.

      The algorithm allows the relay to be used in a time proportioned way using the Time Proportioned output extension.

      The loop tuning parameters can be set at build time and can be adjusted at run time via MQTT.

      The feature is included in Tasmota v9.3.0 onward.

      The PID code adds about 11.1k and the Timeprop code another 1k

      Detailed instructions for setup are in these two xdrv files: tasmota/xdrv_48_timeprop.ino and tasmota/xdrv_49_pid.ino.

      The ESP8266 will run the PID algorithm at 1 cycle per second, which is much faster than is needed for the sort of processes Sonoff devices are usually associated with. It rather clobbers the Tasmota terminal output in the web browser at that rate so it is getting near to the limit. The maximum anyone is likely to need it running at is maybe once every 5 seconds, and the majority of home IoT applications probably nearer once per minute would be sufficient, so the device is well up to the task.

      Help with using the PID algorithm and with loop tuning can be found at http://blog.clanlaw.org.uk/pid-loop-tuning.html. This is directed towards using the algorithm in the node-red node node-red-contrib-pid but the algorithm here is based on the same code so the tuning technique described there should work just the same.

      Due to limited hardware availability this has so far only been tested in a Sonoff Basic with a TH10, and a Sonoff Mini with a DS18B20 connected. If there are any issues running this on other hardware let us know.

      \ No newline at end of file diff --git a/PIR-Motion-Sensors/index.html b/PIR-Motion-Sensors/index.html new file mode 100644 index 0000000000..5be91916b0 --- /dev/null +++ b/PIR-Motion-Sensors/index.html @@ -0,0 +1,7 @@ + PIR Motion Sensors - Tasmota
      Skip to content

      PIR Motion Sensors

      PIR motion sensors, albeit called sensors, are configured as switches in Tasmota since they basically report motion (1) or no motion (0) to the device.

      Most PIR's are single wire and they require connecting to VCC, GND and one GPIO. In this guide we will use GPIO13 as the pin that the PIR output is connected to. See PIN Restrictions on which pins not to use

      Tasmota Settings~

      In Configuration -> Configure Module menu change GPIO13 to Switch1. Step 1

      If there already is a Switch1 simply choose the next in line. Same applies if you're connecting more than 1 PIR on a single device.

      A configured PIR will not appear in the web UI in any form. To make it report like a sensor we need a rule that will send movement triggers to an MQTT topic.

      SwitchMode1 1
      +SwitchTopic 0
      +Rule1 on Switch1#state=1 do publish stat/%topic%/PIR1 ON endon on Switch1#state=0 do Publish stat/%topic%/PIR1 OFF endon
      +Rule1 1
      +
      You can change (PIR1) and the message (ON/OFF) to whatever suits your needs. %topic% is the configured device topic.

      Look in console for motion detection messages [20:24:03] stat/%topic%/PIR1 ON to verify everything is working

      optional: Before using rules configure any GPIO that doesn't have anything connected to it as Relay1.
      This creates a dummy relay which is triggered by the PIR so you can see the changes in the web UI. This method is not recommended for daily use and should only be used for testing.

      A more advanced example of rules with PIRs.

      AM312~

      AM312 works even on 3.3v instead of 5v (like HC-SR501) which makes it perfect for ESP8266 devices without a 5V line (like Sonoff Basic). It is also less prone to false triggers due to Wi-Fi interference.

      Pinout~

      AM312 Pinout

      Pin marked VOUT is connected to a free GPIO pin on the device.

      This PIR goes to off state after a few seconds so we need to use this rule instead of the one in the example.

      Rule1 on Switch1#state=1 do Backlog Publish stat/%topic%/PIR1 ON; RuleTimer1 30 endon on Rules#Timer=1 do Publish stat/%topic%/PIR1 OFF endon
      +
      With this it will stay ON for 30 seconds then send OFF message and the timer restarts every time there's an ON trigger.

      Another configuration option is to change Switchmode to 14 with Pulsetime of 130 (30 seconds on every time the AM312 is triggered)

      Another use case as a hand wave switch.

      HC-SR501~

      Pinout~

      Configuration with HC-SR501 is easiest with Switchmode 1, since this module has a built-in trigger/delay potentiometers and the state remains ON during the trigger period.

      MH-SR602~

      This is a very small version of a PIR that is able to modify the sensitivity and delay by soldering resistors.

      With factory settings this PIR goes to off state after a few seconds so we need to use this rule instead of the one in the example.

      Rule1 on Switch1#state=1 do Backlog Publish stat/%topic%/PIR1 ON; RuleTimer1 30 endon on Rules#Timer=1 do Publish stat/%topic%/PIR1 OFF endon
      +
      With this it will stay ON for 30 seconds then send OFF message and the timer restarts every time there's an ON trigger.

      MH-SR602

      Pinout~

      MH-SR602 MH-SR602

      Panasonic EKMC1603111~

      Set the data pin to Switch n for it to work.

      Datasheet

      \ No newline at end of file diff --git a/PN532/index.html b/PN532/index.html new file mode 100644 index 0000000000..6834284961 --- /dev/null +++ b/PN532/index.html @@ -0,0 +1,22 @@ + PN532 NFC reader - Tasmota
      Skip to content

      PN532 NFC reader~

      This feature is included only in tasmota-sensors and tasmota32 binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_PN532_HSU 
      +#define USE_PN532_HSU                            // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem)
      +// Optional defines, uncomment (remove //) as needed
      +//  #define USE_PN532_DATA_FUNCTION                // Add sensor40 command support for erase, setting data block content (+1k7 code, 388 bytes mem)
      +#endif
      +

      The PN532 is a highly integrated transceiver module for contactless communication at 13.56 MHz based on the 80C51 microcontroller core.

      The datasheet for the PN532 chip is available here.

      Please note that although the datasheet mentions that the PN532 can be used on SPI, I2C and HSUART that only the HSU interface is implemented in the Tasmota driver.

      Configuration~

      #define USE_PN532_DATA_FUNCTION
      This function is experimental. There are limitations because it seems not all cards are supported by this driver and/or the PN532 module. See issue 4941 for more information. We are still researching the

      Wiring~

      As mentioned earlier the PN532 breakout boards usually have pins broken out for all three protocols supported by the PN532 but we are only interested in the HSU interface as that is all the driver currently supports.

      For this reason breakout boards have either micro dip switches as shown in the image below, or they have pads on the PC board which you need to bridge out with solder to select which mode the PN532 will operate in.

      pn532_nfc_hsu_config

      After selecting the correct protocol mode and connecting the HSU TX/RX pins of the PN532 to the pins you configured on your ESP8266 board you can power it up and the PN532 should be detected automatically.

      PN532 ESP
      GND GND
      VCC 3.3V
      SDA GPIOx
      SCL GPIOy

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      • GPIOx to PN532 Rx (139)
      • GPIOy to PN532 Tx (138)

      The module will reboot when you save this configuration. During start-up the following information should be visible in your console output:

      00:00:00 NFC: PN532 NFC Reader detected V1.6
      +
      If the device was not found please check your wiring and configuration and confirm that everything is as it should be.

      Example

      Configured using Wemos D1 mini on pins: D1 (connected to PN532 SCL) and D2 (connected to PN532 SDA)

      PN532 Software Configuration

      Usage~

      Tasmota will scan for a new card detect 4 times per second and if found will report it via immediate telemetry.

      The output on the console will look similar to the below when a new card is detected

      18:23:24 MQT: tele/tasmota/SENSOR = {"Time":"2019-01-10T18:23:24","PN532":{"UID":"94D8FC5F", "DATA":""}}
      +18:23:24 MQT: stat/tasmota/RESULT = {"Event":"Done"}
      +18:23:25 MQT: stat/tasmota/RESULT = {"Event":"Done"}
      +

      The UID of the card/tag is reported and any text stored in BLOCK 1 of a Mifare Classic card or PAGE4-7 of a NTAG card (up to 16 characters in length) is reported in the DATA field of the JSON sent via telemetry. Please note that the DATA field field can contain only printable chars.

      The content of the DATA on BLOCK 1 of a Mifare Classic (PAGE4-7 of a NTAG) card can be set as follows

      Sensor40 WRITE,I LOVE TASMOTA

      Once executed the very next card/tag that is presented to the reader will be programmed accordingly and the data will be retained on the card/tag until either changed or erased.

      To erase the content of the DATA field the following command may be used

      Sensor40 ERASE

      Once executed the very next card/tag that is presented to the reader will have its BLOCK 1 (PAGE4-7 of a NTAG) erased.

      For canceling previous command he following command may be used

      Sensor40 CANCEL

      Once executed will stop waiting next card/tag.

      For NTAG213/215/216, NT3H2111/2211 or compatible are additionally available commands: AUTH, SET_PWD, UNSET_PWD for password operations.

      Warning. The password is a unsigned 32 bit and acknowledge is a unsigned 16 bit values.

      Sensor40 AUTH,password,ack

      Set tasmota password phrase and password acknowledge for authentication.

      Sensor40 SET_PWD or Sensor40 SET_PWD,new_password or Sensor40 SET_PWD,new_password,new_ack

      Write current password and ack / new password and current ack / new password and new acknowledge to a NTAG21x tag.

      Sensor40 UNSET_PWD

      Clear password and ack in a NTAG21x tag.

      Text logging of the above two actions are also presented during the process for information purposes.

      Using the UID and DATA of a presented card~

      When a card is presented to the PN532 under normal operating conditions up to 3 ways of using the data is possible.

      The first is the immediate telemetry generated which looks as follows. For not NTAG21x:

      18:31:39 MQT: tele/tasmota/SENSOR = {"Time":"2019-01-10T18:31:39","PN532":{"UID":"94D8FC5F", "DATA":"ILOVETASMOTA"}}
      +

      For NTAG21x and correct password and acknowledge:

      20:05:48 MQT: tele/tasmota/SENSOR = {"Time":"2023-02-24T20:05:48","PN532":{"UID":"53BB1F05110001","Data":"I love tasmota","Auth":"Ok"}}
      +

      For NTAG21x and correct password and not correct acknowledge:

      20:05:48 MQT: tele/tasmota/SENSOR = {"Time":"2023-02-24T20:05:48","PN532":{"UID":"53BB1F05110001","Data":"I love tasmota","Auth":"NOk"}}
      +

      For NTAG21x and not password protection in a tag:

      20:05:48 MQT: tele/tasmota/SENSOR = {"Time":"2023-02-24T20:05:48","PN532":{"UID":"53BB1F05110001","Data":"I love tasmota","Auth":"None"}}
      +

      Since this is an immediate telemetry generation as opposed to the sensor data you would normally be expected to be presented when the telemetry period occurs, this telemetry data is not directly usable on the device itself. It is generated and immediately transmitted over MQTT and the purpose of this is so that immediate action may be taken by any home automation software you are using with the data obtained from the card/tag as opposed to waiting for the telemetry period to expire and be sent with normal telemetry data.

      For the purpose of using card/tag data on the device itself you will need to use rules along with the events that are caused.

      Example

      Example rule for responding to a specific UID on the device when a card/tag matching a specific UID is presented

      rule1 on PN532#UID=94D8FC5F do power on endon
      +

      Example

      Example rule for responding to a specific DATA content that was previously programmed to one or more cards using the Sensor40 write,xxxx command

      Rule 1on EVENT#PN532_DATA=ILOVETASMOTA do power on endon
      +
      18:41:12 MQT: tele/tasmota/SENSOR = {"Time":"2019-01-10T18:41:12","PN532":{"UID":"94D8FC5F", "DATA":"ILOVETASMOTA"}}
      +18:41:13 MQT: stat/tasmota/RESULT = {"Event":"Done"}
      +18:41:13 MQT: stat/tasmota/RESULT = {"Event":"Done"}
      +18:41:13 RUL: EVENT#PN532_DATA=ILOVETASMOTA performs "power on"
      +18:41:13 MQT: stat/tasmota/RESULT = {"POWER":"ON"}
      +18:41:13 MQT: stat/tasmota/POWER = ON
      +

      Breakout Boards~

      Since the PN532 chip itself is surface mount and requires some external components to operate the best is probably to obtain a breakout board similar to the one below from your favourite online supplier.

      PN532 Breakout Board Module

      Please make sure the breakout board you order has the HSU pins (SCL=TX and SDA=RX) broken out as it will be very difficult to add them manually - Usually they have them but for good measure just make sure.

      \ No newline at end of file diff --git a/PS-16-DZ-Dimmer/index.html b/PS-16-DZ-Dimmer/index.html new file mode 100644 index 0000000000..dace98caf7 --- /dev/null +++ b/PS-16-DZ-Dimmer/index.html @@ -0,0 +1,69 @@ + PS-16-DZ Dimmer - Tasmota
      Skip to content

      PS-16-DZ Dimmer~

      The PS-16-DZ dimmer uses eWeLink app same as Sonoff devices. It's available from Amazon and Aliexpress. It may under different name, but the model number is PS-16-DZ.

      Pics~

      Opened it up, found an ESP8285 and a Nuvoton N76E003.

      Flash and GPIO pins~

      Remove the ESP8285 daughter card DLX-MKCK01 from the main board and flash Tasmota.

      pin1 RST -> N76E003 P0.5/PWM2
      +pin2 TXD -> N76E003 P0.7/RXD
      +pin3 RXD -> N76E003 P0.6/TXD
      +pin4 GPIO13/MTCK -> LED
      +pin5 3.3V
      +pin6 GND
      +

      Communication between ESP8285 and N76E003~

      It uses AT command to communicate between ESP8285 and N76E003 over UART at 19200 baud.

      Turn on the switch from ESP8266:
      +Send from ESP8266
      +AT+UPDATE="sequence":"1528335040032","switch":"on"
      +Receive from MCU
      +AT+RESULT="sequence":1528335040032"
      +Send from ESP8266:
      +AT+SEND=ok
      +After 0.7s
      +Receive from MCU
      +AT+UPDATE="switch":"on","bright":44
      +Send from ESP8266:
      +AT+SEND=ok
      +
      +Turn off the switch from ESP8266:
      +Send from ESP8266
      +AT+UPDATE="sequence":"1528335118327","switch":"off"
      +Receive from MCU
      +AT+RESULT="sequence":1528335118327"
      +Send from ESP8266:
      +AT+SEND=ok
      +After 0.7s
      +Receive from MCU
      +AT+UPDATE="switch":"on","bright":44
      +Send from ESP8266:
      +AT+SEND=ok
      +
      +Change brightness to 44 from ESP8266:
      +Send from ESP8266:
      +AT+UPDATE="sequence":"1528335181502","bright":44
      +Receive from MCU
      +AT+RESULT="sequence":1528335181502"
      +Send from ESP8266:
      +AT+SEND=ok
      +After 0.7s
      +Receive from MCU
      +AT+UPDATE="switch":"on","bright":44
      +Send from ESP8266:
      +AT+SEND=ok
      +
      +Turn on the switch with faceplate button:
      +Received from MCU
      +AT+UPDATE="switch":"on","bright":44
      +Send from ESP8266:
      +AT+SEND=ok
      +
      +Turn off the switch with faceplate button:
      +Received from MCU
      +AT+UPDATE="switch":"off","bright":44
      +Send from ESP8266:
      +AT+SEND=ok
      +
      +Change the brightness to 52 with faceplate button:
      +Received from MCU
      +AT+UPDATE="switch":"on","bright":52
      +Send from ESP8266:
      +AT+SEND=ok
      +
      +Holding the middle button on faceplate to reset the device:
      +Received from MCU
      +AT+SETTING=enterESPTOUCH
      +Send from ESP8266:
      +AT+SETTING=enterESPTOUCH
      +
      The sequence number is a 13 digits unix timestamp (in milliseconds). The data after "bright" is the brightness. The maximum of the brightness is 100 and the minimum is 10. There is one byte 0x1B at the end of each AT command.

      \ No newline at end of file diff --git a/PWM-dimmer-switch/index.html b/PWM-dimmer-switch/index.html new file mode 100644 index 0000000000..aa9eef60eb --- /dev/null +++ b/PWM-dimmer-switch/index.html @@ -0,0 +1 @@ + PWM Dimmer - Tasmota
      Skip to content

      PWM Dimmer~

      PWM dimmer is supported in standard tasmota.bin

      To enable PWM dimmer operation, select the PWM Dimmer module.

      PWM Dimmer module adds support for PWM dimmer switches and devices with one or more buttons that control devices in a device group. The brightness of the load for PWM dimmers is controlled by a PWM GPIO pin. They typically have power, up and down buttons, a powered-on LED, five brightness LEDs and another status LED. The SD0x dimmer from Martin Jerry, Acenx, Tessan or NTONPOWER is an example of such a device.

      Martin JerryAcenxTessanNTONPOWER

      Any other device with one or more buttons, such as any typical Tasmota-capable wall switch, can make use of the PWM Dimmer module to control the power, brightness and light channels of one or more device groups. For single-button devices or multi-button devices with remote device mode enabled, only the operations controlled by the power button in the instructions below apply. Examples include:

      Treatlifegosund

      PWM Dimmer Operation~

      Pressing and releasing the power button toggles the power on/off. If the toggle turns the power on, the light is returned to the last brightness it was set to. If Fade is enabled, the light is faded on/off at the rate defined by the Speed setting.

      When the power is on, holding the down or up button decreases/increases the brightness. The brightness can also be changed using just the power button. When the power is on, holding the power button alternately increases or decreases the brightness. Initially, holding the power button increases the brightness. Releasing and then holding the power button again decreases the brightness.

      When the power is off, pressing and releasing the down or up button turns the power on at a temporary brightness of the low/high levels set by the BriPreset command. Turning the power on at the low preset can also be accomplished by holding the power button while the power is off. The brightness presets are intended to enable quickly turning on a light to a dim or bright level without changing the normal desired brightness. Turning the power on to a preset does not change the brightness the light will be set to when the switch is turned on the next time. For example, if the light is on and you adjust the brightness to 80 and then turn the light off, when you turn it back on, the brightness will return to 80. If you turn the power off again and then press the down button, the light will be turned on at the brightness defined by the low preset. If you then turn the light off and on again, the brightness will return to 80.

      If there are LEDs defined in the template, they are turned on to indicate the current brightness. More LEDs are turned on at higher brightnesses. SetOption86 enables/disables the LED timeout. If SetOption86 is enabled, the LEDs turn off five seconds after the last change in brightness. Note that the lowest LED and the blue power LED are always on when the power is on. The LED timeout can also be enabled/disabled by holding the power button while tapping (pressing and releasing quickly) the down button.

      The LedLink LED can be used as a nightlight/powered-off indicator. SetOption87 enables/disables turning the LedLink LED on when the power is off. The powered-off indicator can also be enabled/disabled by holding the power button and tapping the up button.

      Holding the power button and then holding the down or up button publishes an MQTT EVENT command. The topic follows the format of the Full Topic with a subtopic of EVENT (ex. cmnd/LightSwitch1/EVENT). The MQTT payload is Trigger#, where # is 1 if the down button is held or 2 if the up button is held. These triggers can be used in rules on remote devices (ON Event#Trigger1) or by automation software to trigger automations such as scene changes. For example, the Event topic Trigger1 payload could trigger the automation software to turn on the previous scene in a list and the Trigger2 payload could trigger the automation software to turn on the next scene in a list.

      Holding the power button, tapping the down button and then tapping or holding the down or up button sends a device group message to set CW/RGB/RGBW/RGBCW lights in the device group to the previous/next fixed color. The command is sent/value is adjusted once every .75 seconds for as long as the button is held. The color sequence is red, green, blue, orange, light green, light blue, amber, cyan, purple, yellow, pink, white using RGB channels; cold white using CT channels; and warm white using CT channels.

      Holding the power button, tapping the up button and then tapping or holding the down or up button publishes an MQTT Event command. The command is sent once every .75 seconds for as long as the button is held. The MQTT topic is as described above. The MQTT payload is Trigger#, where # is 3 if the down button is held or 4 if the up button is held.

      Pressing and releasing the power button and then holding the power button publishes an MQTT Event command. The command is sent once every .75 seconds for as long as the button is held. The MQTT topic is as described above. The MQTT payload is Trigger#, where # is 5.

      Button presses and holds execute the normal ButtonTopic and Rule processing. If ButtonTopic is set and SetOption61 is 0 or a the button press/hold matches a rule, the button press/hold is ignored by PWM Dimmer. Operations invoked by holding the power button in combination with the up/dowm buttons cannot be overridden by rules. Standard Tasmota multi-press button presses operate as normal.

      PWM Dimmer uses the Light module to control the PWM. Brightness levels are rescaled to PWM values between the dimmer_min and dimmer_max values specified with DimmerRange. Most LED bulbs do not show a significant difference between PWM value of 1 and PWM value of 100. This results in the lower 10% of the dimmer range having no effect. For best results, DimmerRange <dimmerMin> value should be set to the highest value that results in the lowest bulb brightness (Typically in the range of 8 - 18).

      When Device Groups are enabled, the PWM Dimmer brightness presets are kept in sync across all switches in the group. The powered-off LED and LED timeout settings are specific to each switch. Changing them does not replicate the change to the other switches in the group.

      When CW/RGB/RGBW/RGBCW lights are in the same device group as the PWM Dimmer device, use the PWMDimmerPWMs command to define the PWM (channel) count of the lights. This allows the PWM Dimmer module to correctly determine the brightness (dimmer) level and allows the color of all the lights in the device group to be controlled from the PWM Dimmer device.

      Commands~

      Command Parameters
      BriPreset <low>,<high> = set brightness low and high presets
      1..255 = set brightness preset
      + = increase brightness preset
      - = decrease brightness preset
      LedMask Set a bitmask specifying which LEDs are used to indicate the current brightness. LEDs not included in the bitmask can be controlled with LedPower.
      <bitmask> = bitwise value representing each LED. Values may be entered as either hexadecimal or decimal values (e.g., 0xFFFF = 65535). Note that LED 0 is tied to the relay and is always used to indicate the first level of brightness. 0xFFFF (= 1111 1111 1111 1111) All LEDs are used to indicate the brightness (default)
      Ex.: LedMask 3 = Use LEDs 0, 1 and 2 to indicate the brightness.
      PWMDimmerPWMs Set the PWM (channel) count of lights in the device group controlled by the module (CW=2, RGB=3, RGBW=4, RGBCW=5).
      SetOption86 Set brightness LED timeout
      0 = disable timeout (default)
      1 = enable timeout
      SetOption87 Set powered-off LED (nightlight)
      0 = disable powered-off LED (default)
      1 = enable powered-off LED
      SetOption88 Set remote device mode
      0 = disable remote device mode(default)
      1 = enable remote device mode

      Remote Device Mode~

      Remote device mode allows PWM Dimmer switches to control remote devices. With remote device mode enabled, each button controls a different device.

      Remote device mode is included in the default Tasmota binary. To include remote device mode support in other builds, define USE_PWM_DIMMER_REMOTE and USE_DEVICE_GROUPS in your user_config_override. Remote device mode support requires device group support. Remote device mode support adds 1K to the code size in addition to the code size required for device groups support.

      To enable remote device mode, execute SetOption88 1 (the device will restart). Each remote device must be running firmware with device group support and have remote device support enabled. Remote devices do not need to be built with PWM Dimmer support nor do they need to be switches.

      If a remote device also uses the PWM Dimmer module, the device acts like a 3-way dimmer switch and may or may not have a load connected to it. All PWM Dimmer switches in the device group can control the power, brightness and color of one or more smart lights with Tasmota with device group support loaded on them.

      When remote device mode is enabled, button 1 is the power button for the local device while buttons 2 and 3 are the power buttons for remote devices. Group names for buttons 2 and 3 are set by the DevGroupName2 and DevGroupName3 commands respectively. Note that the button numbers are defined by the module template and can be in any physical order on the switch (button 1 can be defined as the top button, the middle button or the bottom button). Button combinations that publish MQTT Event commands use a topic in the format cmnd/%group-topic%/EVENT.

      While holding a button, the other two buttons act like the down and up buttons for the remote device associated with the first button pressed. All the functions performed by the down and up buttons in non-remote device mode are available in remote device mode. While holding button 1, button 2 performs the functions of the down button and button 3 performs the functions of the up button. While holding button 2, button 1 performs the functions of the down button and button 3 performs the functions of the up button. While holding button 3, button 1 performs the functions of the down button and button 2 performs the functions of the up button.

      \ No newline at end of file diff --git a/PZEM-0XX/index.html b/PZEM-0XX/index.html new file mode 100644 index 0000000000..7fffc2d2f4 --- /dev/null +++ b/PZEM-0XX/index.html @@ -0,0 +1,2 @@ + PZEM-0xx power monitor - Tasmota
      Skip to content

      PZEM-0xx power monitor~

      PZEM is a dedicated separate energy monitor, device calibration in Tasmota is not supported.

      PZEM-004~

      The PZEM-004T together with a Sonoff Basic provide a good clamp on energy monitor.

      Parts needed~

      • Sonoff Basic
      • PZEM-004T
      • Resistor 1k
      • Enclosure
      • Power cable

      Preparation~

      Install Tasmota on the Sonoff Basic and confirm it is functional before connecting the PZEM-004T to its serial interface.

      Hardware connections~

      As the PZEM-004T RX optocoupler series resistor (1K ohm, R15 for v.1 .0 and R8 for v.3.0 ) is designed for 5V, that resistor value had to be reduced in order to achieve the current for driving the RX optocoupler diode. This can be accomplished by soldering a 1k resistor between the joints shown below (modification works for version v.1.0 and v.3.0). The resistor has to be connected between VDD (5V/3.3V) terminal and the RX opto terminal 1.

      PZEM-004T v.1.0

      PZEM-004T v.3.0 It can be used a SMD resistor 102 or 1001 (1K) soldered near/parallel with R8 or a normal resistor (THT) similar to that used on the image of v.1.0 The resistor is placed in different place on v.3.0 because the optocouplers RX and TX are reversed compared to v.1.0

      Connect the serial interface of the Sonoff Basic with the serial interface of the PZEM-004T. See pictures regarding used colors and connections.

      • 3V3/5V Red
      • Rx Yellow
      • Tx Green
      • Gnd Grey

      (Image re-used from https://www.instructables.com/id/Use-Homie-Firmware-to-Drive-Sonoff-Switch-Module-E/ Thanks @amayii0)

      If you need 5V you can use directly from Sonoff (for something else) but do not connect to PZEM logic because this will result in a big flash (kaboom!, the sonoff LIVE line may reach the PZEM NEUTRAL or viceversa). Using 5V from Sonoff for PZEM TTL port is safe but the resistor mod explained above must be undone and another mod is needed for dropping the PZEM TX line from 5V to 3.3V. So, the simplest way is to use 3.3V from Sonoff to 5V TTL terminal of the PZEM and the resistor mod explained in the above images.

      Cut the power cable in two and connect the input wires to both Sonoff Basic and PZEM-004T. Route one of the power output wires through the PZEM-004T core and connect the output wires to the Sonoff Basic output.

      As most parts are connected to high voltage AC it is recommended to fit the hardware in a solid enclosure.

      Software configuration~

      Configure the GPIO's for hardware serial connection as shown below.

      IMPORTANT: If using the connections as following, the communication works in all cores due to TASMOTA using hardware serial. If the user wants to use other GPIOs for communication, TASMOTA will emulate a serial interface using software serial. This feature does not work using core 2.3.0 due to insufficient RAM. To use the software serial feature, you must use a core version of 2.4.2 or greater.

      Device Template
      PZEM-004T version prior to V3:

      {"NAME":"HW-655 PZEM","GPIO":[0,62,0,63,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}

      PZEM-004T version V3~

      {"NAME":"HW-655 PZEM","GPIO":[0,62,0,98,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}


      The PZEM-004T together with a HW-655 Relay provide a good clamp-on energy monitor for a 240V clothes dryer.

      Parts needed~

      Preparation~

      Install Tasmota on the ESP-01 and confirm it is functional before connecting the PZEM-004T to its serial interface. Use of pins other that the default hardware serial GPIO (01 & 03) in order for TASMOTA to emulate a serial interface using software serial.

      Hardware connections~


      Connect the serial interface of the HW-655 with the serial interface of the PZEM-004T.


      As most parts are connected to high voltage AC it is recommended to fit the hardware in a solid enclosure.


      Software configuration~

      Device Template
      PZEM-004T version prior to V3:

      {"NAME":"HW-655 PZEM","GPIO":[0,62,0,63,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}

      PZEM-004T version V3~

      {"NAME":"HW-655 PZEM","GPIO":[0,62,0,98,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}

      Use the module template to configure the GPIO's for hardware serial connection.

      IMPORTANT: If using the connections as following, the communication works in all cores due to TASMOTA using hardware serial. If the user wants to use other GPIOs for communication, TASMOTA will emulate a serial interface using software serial. This feature does not work using core 2.3.0 due to insufficient RAM. To use the software serial feature, you must use a core version of 2.4.2 or greater.

      Connected Power Meter using PZEM-004T, Wemos D1 Mini and a 1602 I2C display~

      Parts needed~

      • Wemos D1 Mini
      • PZEM-004T
      • 1kOhm Resistor (optional - see alternate wiring)
      • Enclosure
      • 5V buck converter power supply (search for "700ma 3.5w 5v" on usual stores...)
      • I2C 1602 LCD Display (I had issues with green one, I2C address 0x3F, while no problems with blue ones, address 0x27)
      • Mains Power cable
      • Mammuth Clamps

      Preparation~

      You need to compile your own Tasmota firmware as none of the pre-compiled binaries have support for display and PZEM module.

      Set up your preferred IDE as described in wiki

      Enable IDE to Use Custom Settings~

      Create user_config_override.h in the tasmota folder and paste the contents of this sample configuration file.

      PlatformIO~

      • Rename platformio_override_sample.ini. to platformio_override.ini
      • Enter platformio run -e <variant-name>
        Examples:
      • platformio run -e tasmota-sensors
      • platformio run -e tasmota-DE

      Arduino IDE~

      • Edit my_user_config.h. Uncomment the statement by removing the "//" in front of the line: #define USE_CONFIG_OVERRIDE
      • Click compile

      Flash the binary on the Wemos D1 Mini and confirm it is functional before connecting the PZEM-004T to its serial interface.

      Tasmota Parameter Configuration~

      Device Template
      PZEM-004T version prior to V3:

      {"NAME":"HW-655 PZEM","GPIO":[0,62,0,63,6,5,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}

      PZEM-004T version V3~

      {"NAME":"HW-655 PZEM","GPIO":[0,62,0,98,6,5,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}

      • use I2CScan to detect your device address
      • use DeviceAddress XXX (where XXX is the decimal converted address found) to set the I2C address
      • set TelePeriod 10 to have the display refresh every 10 seconds (you can't go under this value)
      • set DisplayModel 1, and DisplayMode 0
      • finally, add a Rule to display values (I choose these):
        Rule1 ON Tele-ENERGY#Power DO DisplayText [z] [x1y0]%value%W ENDON ON Tele-ENERGY#Today DO DisplayText [x8y0]%value%Wh ENDON ON Tele-ENERGY#Voltage DO DisplayText [x1y1]%value%V ENDON ON Tele-ENERGY#Current DO DisplayText [x8y1]%value%A ENDON
        +
      • remember to enable the rule, with Rule1 1

      Images and Wiring diagram~

      DANGER - MAINS VOLTAGE. Be sure to crimp connectors and use heat-shrinking tube wherever possible/needed, and tightly secure any screw.



      How it looks, from web GUI:



      How it looks, from enclosure:

      You can set the contrast using the little trimmer/pot on back of the display. I cut a bit of the corners from the display to have it flush with border, and used two hexagonal plastic standoffs with nuts and bolts to secure it to transparent top.

      Mains IN, mains OUT, all sealed:



      Wiring Diagram: * Check images below for more information about the 1kOhm resistor needed to use 3.3V instead of 5V for the PZEM-004T serial connection.

      PZEM-004T v.1.0

      PZEM-004T v.3.0

      Calibration~

      Per Theo - As the PZEM is a dedicated energy monitor, device calibration in TASMOTA is currently not supported.

      PZEM-016~

      ⚠ ⚠ ⚠
      DO NOT PERFORM THIS MODIFICATION WITHOUT REMOVING POWER FROM THE PZEM FIRST!
      ⚠ ⚠ ⚠

      Note: the PZEM-016 TTL output is at 5V signal levels. There are varying schools of thought on whether the ESP82xx has 5V tolerant GPIO. You may want to use a level shifter for the serial communications signals to bring them to the recommended 3.3V.

      PZEM-016 modules can be converted from RS485 to TTL serial level devices by simply removing the internal MAX485 chip and adding two internal jumper wires. This will bring the serial port connections out via the four-pin terminal block. Pin A is now TTL serial out (Tx) and pin B TTL serial in (Rx). The modification retains the optical isolation used by the PZEM for safety to ensure no high voltages on the outputs.

      You can use a voltage level shifter to power the ESP82xx from the PZEM-016 module's 5V power. This may also require a 470uf 35V capacitor across the 5V line to work reliably.

      Tasmota Configuration~

      It is recommended to use GPIO1/GPIO3 or GPIO13/GPIO15 for the most reliable serial communications. When using other GPIOs software serial will be activated and used.

      GPIO Component PZEM
      1 PZEM0XX Tx (62) Pin B
      3 PZEM016 Rx (98) Pin A
      \ No newline at end of file diff --git a/Peripherals/index.html b/Peripherals/index.html new file mode 100644 index 0000000000..70190cf87d --- /dev/null +++ b/Peripherals/index.html @@ -0,0 +1,20 @@ + Peripherals - Tasmota
      Skip to content

      Peripherals~

      Peripherals are sensors, displays, controllers, LEDs and other devices wired to available GPIO pins of your device

      Tip

      A peripheral must have correctly wired power, GND and data pins to the device prior to booting in order for Tasmota to detect it and initialize it properly.

      Supported peripherals~

      Tasmota Settings~

      Tasmota allows for easy selection of peripherals and assignment to GPIO pins.

      Configuration is possible in the webUI Configuration - Configure Module page

      or by using commands: Module and GPIO, or Template.

      Module~

      First select desired module for the device (Wait for the restart). Depending on the type of Module, only certain GPIO pins are user configurable. Module Generic (18) has all the GPIOs configurable.

      Modules shows supported modules

      GPIO~

      Assign a component to a GPIO.

      • GPIO14 2 configures sensor AM2301 to GPIO14_
      • Backlog GPIO14 5; GPIO4 6 sets I2C SCL to GPIO14 and I2C SDA to GPIO4 Tasmota will auto-detect all connected and supported I2C devices. If you have conflicting I2C addresses see I2CDEVICES

      GPIOs All shows list of all available components by name and index

      For a peripheral to show up you may need to power cycle your device instead of a soft restart.

      Template~

      Instead of using Module and GPIO you can define everything using Template. Read more...

      Additional Options~

      Measurement Units~

      Temperature units can be set to Celsius or Fahrenheit with SetOption8 command.

      Pressure units can be set to hPa or mmHg with SetOption24 command.

      Update Interval~

      To change the update interval (TelePeriod) of MQTT messages change the TelePeriod. Default interval is 300 seconds but can be set between 10 and 3600 seconds.
      TelePeriod 10 will set the update interval to 10 seconds, so the sensor will update 6 times a minute.

      Peripheral Specific~

      Some peripherals offer, or even require, additional commands. See Sensor commands page for peripheral specific commands.

      Tip

      To make a link between the different naming schemes the Pin Definition overview in the ESP8266 wiki is quite helpful.

      Examples~

      Connect switch~

      If you take a Sonoff Basic and connect a switch between pin4 (GND) and pin5 (GPIO14) of the 5 pin programming header you now have a second switch connected to the device. You can set this through the module config page as option Switch1 (9) or from the command line with gpio14 9.

      See Buttons and Switches for more information.

      Connect jack~

      Instead of connecting a switch, you could connect a 4-pin 2.5mm jack, with the pins wired:

      Jack Pin ESP8266
      tip 5 GPIO14
      R1 no connection
      R2 1 GND
      R3 4 3.3V

      You can then plug a sensor into the jack like you would to a Sonoff TH and define what sensor you have connected to GPIO14.

      Restrictions~

      Danger

      If you can avoid it, don't use GPIOs: 0, 1, 2, 6-11, 15 and 16. That leaves 4, 5, 12, 13, 14 as GPIOs without any constraints. 3 being RX is also good to avoid (PWM is not working on this GPIO).

      Others can be used but you have to mind the constraints outlined in this document.

      Voltage and Current~

      Danger

      The ESP8266 is a 3.3V microcontroller, so its I/O operates at 3.3V as well. The pins are not 5V tolerant, applying more than 3.6V on any pin will release the magic smoke (fry the chip). The maximum current that can be drawn from a single GPIO pin is 12mA.

      Power Supply~

      It is important to have a reliable power supply

      The power supplied to the device is one of the most important elements for stable device operation. Many devices on the market have barely adequate power supplies for normal operation. Connected peripherals may strain the ability of the power supply on the device to deliver appropriate power to all the components, both on-board as well as externally connected.

      Voltage regulation issues typically result in fatal exception fault code 1. You must ensure that the device receives sufficient power (current and appropriate voltage level). Take into account the current that each wired component (f.e. sensor) will draw from the device itself.

      Electrical Considerations~

      When you switch a GPIO pin to an input and hang a long wire off of it, that wire can pick up stray signals and cause the voltage on the GPIO pin to vary. This can cause the system to think the switch has changed.

      To fix this, there are several things you can do.

      1. add a pull-up resistor
      2. add a bypass capacitor
      3. shielding on the wire
      4. use twisted pair wiring

      A pull-up resistor is a resistor connected between the GPIO pin and 3.3v. The exact value of this is not critical, 4.7k is a common value to use, as is 10k. This ensures that when the switch it open, the GPIO pin will go high.

      A bypass capacitor is a small (pF range) capacitor that is connected between the GPIO and ground. This provides a path for any radio signals that are picked up by the wire to go to ground and not confuse the system.

      Shielding or using twisted pair wiring are other ways to reduce the effect of radio signals on the system.

      Example for 10K Resistor (issue#2708)

      ESP8266 In Depth~

      Complete document available from https://tttapa.github.io/ESP8266/Chap04%20-%20Microcontroller.html

      Digital I/O~

      Just like a normal Arduino, the ESP8266 has digital input/output pins (I/O or GPIO, General Purpose Input/Output pins). As the name implies, they can be used as digital inputs to read a digital voltage, or as digital outputs to output either 0V (sink current) or 3.3V (source current).

      Usable Pins~

      The ESP8266 and ESP8285 have 17 GPIO pins (0-16) but several are reserved or have constraints. Do not use any of the reserved pins. If you do, you might crash your program. On the ESP8266, six pins (GPIO 6 - 11) are used to interface the flash memory (the small 8-legged chip usually right next to the ESP8266). The ESP8285 has its flash memory integrated into the chip which frees up GPIO 9 and 10.

      GPIO 1 and 3 are used as TX and RX of the hardware Serial port (UART), so in most cases, you can’t use them as normal I/O while sending/receiving serial data. GPIO 1, 2 and 3 will cause boot failure if LOW on boot - use with care.

      best ESP8266 pins

      Boot Mode Pins~

      Some I/O pins have a special function during boot: They select 1 of 3 boot modes:

      GPIO15 GPIO0 GPIO2 Mode
      0V 0V 3.3V Uart Bootloader
      0V 3.3V 3.3V Boot sketch (SPI flash)
      3.3V x x SDIO mode (not used for Arduino)

      Note: you don’t have to add an external pull-up resistor to GPIO2, the internal one is enabled at boot.

      We have to be sure that these conditions are met by adding external resistors, or the board manufacturer of your board has added them for you. This has some implications, however:

      GPIO15 is always pulled low, so you can’t use the internal pull-up resistor. You have to keep this in mind when using GPIO15 as an input to read a switch or connect it to a device with an open-collector (or open-drain) output, like I²C. GPIO0 is pulled high during normal operation, so you can’t use it as a Hi-Z input. GPIO2 can’t be low at boot, so you can’t connect a switch to it. Internal pull-up/-down resistors GPIO 0-15 all have a built-in pull-up resistor, just like in an Arduino. GPIO16 has a built-in pull-down resistor.

      PWM~

      ESP8266 Unlike most Atmel chips (Arduino), the ESP8266 doesn’t support hardware PWM, however, software PWM is supported on all digital pins. The default PWM range is 10-bits @ 1kHz, but this can be changed (up to >14-bit@1kHz). Check Restrictions.

      ESP8266 has only software and supports 5 PWM channels. PWM and PWMi GPIOs are used in two modes depending on SetOption15: either as lights or as pure PWM.

      ESP32 has hardware PWM support, named ledc, up to 16 channels depending on CPU type. You can mix lights and pure PWM channels. The first 5 PWM are reserved for lights, unless SetOption15 0. For pure PWM GPIOs, you can assign any PWM number, they don't need to be continuous. For example you can use PWM 1/2/3 for a 3-channel RGB light, and PWM 6 & PWM 10 for pure PWM at the same time.

      CPU type PWM channels
      ESP32 16 channels
      ESP32S2 8 channels
      ESP32C3 6 channels

      Channels are assigned to GPIOs in a first-in-first-serve way, and PWM GPIOs are assigned first. If ledc channel are exhausted, an error will appear in logs.

      The following GPIOs use ledc PWM channels:

      GPIO type Description
      PWM or PWMi PWM 1..5 are used for lights, PWM O6..11 are general purpose PWM.
      LedPwmMode Assigns a Led GPIO to a PWM channel
      Buzzer If BuzzerPwm is used
      Backlight PWM backlighting for displays
      XCLK Used as a clock generator for webcam

      Example of PWM console output with 16 PWM assigned. By default PWM range is 0..1023.

      RSL: RESULT = {"PWM":{"PWM1":410,"PWM2":286,"PWM3":286,"PWM4":0,"PWM5":0,"PWM6":0,"PWM7":0,"PWM8":0,"PWM9":0,"PWM10":0,"PWM11":0,"PWM12":0,"PWM13":0,"PWM14":0,"PWM15":0,"PWM16":0}}
      +

      Auto-phasing of PWM~

      (ESP32 only) By default, phases of consecutive PWM are disaligned so that a PWM pulses starts when the pulse of the previous PWM channels ends. This helps in distributing over time all pulses and have a smoother effect on power supply.

      With auto-phasing: PWM_autophase

      You can revert this with SetOption134 1; all phases are synces and all pulses start at the same moment. PWM_sync

      H-bridge~

      H-bridge is an electronic circuit that switches the polarity of a voltage applied to a load. It uses 2 PWM outputs to control the current sent to each polarity.

      When auto-phasing is enabled, you can use 2 consecutive PWM to drive a H-bridge siunce PWM phases don't overlap - under the condition that the sum of both PWM don't exceed 1023.

      Important: you must always ensure that the sum of both PWM channels is less or equal than 1023. Values over this threshold can damage the circuit.

      Analog Input~

      The ESP8266 has a single analog input, with an input range of 0 - 1.0V. If you supply 3.3V, for example, you will damage the chip. Some boards like the NodeMCU have an on-board resistive voltage divider, to get an easier 0 - 3.3V range. You could also just use a trimpot as a voltage divider.

      The ADC (analog to digital converter) has a resolution of 10 bits.

      Communication~

      Serial~

      The ESP8266 has two hardware UARTS (Serial ports): UART0 on pins 1 and 3 (TX0 and RX0 resp.), and UART1 on pins 2 and 8 (TX1 and RX1 resp.), however, GPIO8 is used to connect the flash chip. This means that UART1 can only transmit data.

      UART0 also has hardware flow control on pins 15 and 13 (RTS0 and CTS0 resp.). These two pins can also be used as alternative TX0 and RX0 pins.

      I²C~

      ESP8266 doesn’t have a hardware TWI (Two Wire Interface) but it is implemented in software. This means that you can use pretty much any two digital pins. By default, the I²C library uses pin 4 as SDA and pin 5 as SCL. (The data sheet specifies GPIO2 as SDA and GPIO14 as SCL.) The maximum speed is approximately 450kHz.

      SPI~

      The ESP8266 has one SPI connection available to the user, referred to as HSPI. It uses GPIO14 as CLK, 12 as MISO, 13 as MOSI and 15 as Slave Select (SS). It can be used in both Slave and Master mode (in software).

      GPIO Overview~

      NodeMCU Labelled Pin GPIO# Function State Restrictions
      D3 0 Boot mode select 3.3V No Hi-Z
      D10 1 TX0 - Not usable during Serial transmission - Boot will fail if LOW at boot
      D4 2 Boot mode select TX1 3.3V (boot only) Don’t connect to ground at boot time - boot will fail. Sends debug data at boot time
      D9 3 RX0 - Not usable during Serial transmission
      D2 4 SDA (I²C) - -
      D1 5 SCL (I²C) - -
      x 6 - 8 Flash connection x Not usable, and not broken out
      x 9, 10 Flash connection * Only available on the ESP8285
      x 11 Flash connection x Not usable, and not broken out
      D6 12 MISO (SPI) - -
      D7 13 MOSI (SPI) - -
      D5 14 SCK (SPI) - -
      D8 15 SS (SPI) 0V Pull-up resistor not usable (extern pull down resistor)
      D0 16 Wake up from sleep - No pull-up resistor, but pull-down instead Should be connected to RST to wake up
      \ No newline at end of file diff --git a/Pinouts/index.html b/Pinouts/index.html new file mode 100644 index 0000000000..6a8a0d809d --- /dev/null +++ b/Pinouts/index.html @@ -0,0 +1 @@ + Wi-Fi Module Pinouts - Tasmota
      Skip to content

      Wi-Fi Module Pinouts~

      ESP82xx Based~

      ESP8266 and ESP8285~

      ESP8266 pinout

      ESP-12E/ESP-12F~

      ESP-12 pinout

      ESP-12S~

      ESP-12S pinout

      ESP-WROOM-02~

      ESP-WROOM-02 Pinout

      LM1~

      LM1 Pinout

      LM2~

      LM2 Pinout

      TYWE1S~

      TYWE1S Pinout

      TYWE2S~

      TYWE2S Pinout

      TYWE2L~

      TYWE2L Pinout

      TYWE3S~

      TYWE3S Pinout TYWE3S Pinout

      TYWE3L~

      TYWE3L Pinout

      TYLC4~

      TYLC4TYLC4 Pinout

      TYLC5~

      TYLC5TYLC5 Pinout

      TYWE5P~

      TYWE5PTYWE5P Pinout

      PSF-B85/PSF-B01/PSF-B04~

      PSF-B Pinout

      ESP8266-S3~

      ESP8266-S3 Pinout

      WT8266-S1~

      WT8266-S1

      449A-ECOPLUGS~

      EcoPlugs 449A Pinout
      Datasheet

      CUCO-Z0-V1.1 (Used by Gosund)~

      Overview Front Back

      ESP32 Based~

      ESP32-CAM~

      ESP32-CAM Pinout

      ESP32-S~

      ESP32-S Pinout

      ESP32-SOLO-1~

      ESP32-SOLO-1 Pinout

      ESP32-WROVER~

      Includes ESP32-WROVER-B and ESP32-WROVER-BI

      ESP32-WROVER Pinout

      ESP32-WROOM-32x~

      ESP32-ESP-WROOM-32 Pinout

      ESP32-S~

      ESP-12K~

      ESP-12K Pinout

      ESP32-C3~

      ESP-C3-12F~

      ESP-C3-12F Pinout

      ESP-C3-32S~

      ESP-C3-32S Pinout

      ESP-C3-13~

      ESP-C3-13 Pinout

      ESP-C3-01M~

      ESP-C3-01M Pinout

      Devices~

      Sonoff Mini~

      Sonoff Mini Pinout

      Sonoff TH~

      TH GPIOs

      Sonoff Basic~

      Basic GPIOs

      Digoo DG-SP202 / OxaOxe NX-SP202~

      Digoo DG-SP202 / Oxaoxe NX-SP202 Pinout (Found out by tracing the PCB copper tracks of the unknown ESP module. Not verified, except GPIO0, RX, TX, Vcc and GND which have successfully been used for flashing.)

      \ No newline at end of file diff --git a/PlatformIO-CLI/index.html b/PlatformIO-CLI/index.html new file mode 100644 index 0000000000..7ee27c3681 --- /dev/null +++ b/PlatformIO-CLI/index.html @@ -0,0 +1 @@ + PlatformIO CLI - Tasmota
      Skip to content

      How to flash the Tasmota firmware onto a device using the platformio command line interface. This manual was tested on Ubuntu 17.10.

      Prerequisites~

      Install Python and PIP~

      sudo apt-get install python-pip

      Install PlatformIO CLI~

      sudo pip install -U platformio

      Download the Tasmota source code~

      Either download the latest Tasmota Source code from https://github.com/arendst/Tasmota/ and extract it or clone the Git repository:

      git clone https://github.com/arendst/Tasmota.git

      Select the environment~

      The default environment configuration can be used to easily generate Tasmota firmware variants (tasmota, sensors, display, etc.). If you're not sure which binary is the right one for you, consult the builds table or just start with tasmota.bin. To build and/or flash exactly one of these, use the -e command line argument (e.g., -e tasmota-sensors).

      Compile and upload~

      Once all the prerequisites are in place, compiling and uploading is one simple command. Execute this from within the Tasmota source code directory:

      platformio run -e <variant> --target upload --upload-port <port>

      Just make sure to replace <port> with the actual serial port your device is connected to. On Windows these interfaces are named COM ports (COM1, COM2, etc.). On Linux these interfaces are named TTY ports (e.g., /dev/ttyUSB0, /dev/ttyUSB1, etc.)].

      Depending on your configuration your user account may need to be in the dialout group.

      \ No newline at end of file diff --git a/PlatformIO/index.html b/PlatformIO/index.html new file mode 100644 index 0000000000..8692b47e9f --- /dev/null +++ b/PlatformIO/index.html @@ -0,0 +1 @@ + PlatformIO - Tasmota
      Skip to content

      PlatformIO

      How to setup and configure PlatformIO for Tasmota compilation and upload.

      Download PlatformIO~

      Download PlatformIO from https://platformio.org/

      Install PlatformIO~

      Install PlatformIO to a known folder.

      Download Tasmota~

      Download the latest Tasmota Source code from https://github.com/arendst/Tasmota and unzip to another known folder.

      Compile Tasmota~

      Choose the Tasmota variant from the Platformio menu.

      Upload Tasmota~

      PlatformIO uses the serial interface to upload the firmware to your device. On Windows these interfaces are named COM ports (COM1, COM2, etc.). On Linux these interfaces are named TTY ports (e.g., /dev/ttyUSB0, /dev/ttyUSB1, etc.).

      Put device into programming mode~

      When performing a firmware upload do not connect the device to AC. Use a 3.3v DC power supply such as that provided by your serial programming adapter.

      Put the device in programming mode by grounding pin GPIO0 and then applying power (e.g., connecting your computer to the serial adapter). Grounding pin GPIO0 can often be achieved by pressing the button on the device or using a wire between GPIO0 and GND if the button is not available. Deviations may apply.

      Perform serial upload~

      Select Upload from the menu.

      NOTE: For a proper device initialization after first firmware upload power down and power up the device.

      \ No newline at end of file diff --git a/Power-Monitoring-Calibration/index.html b/Power-Monitoring-Calibration/index.html new file mode 100644 index 0000000000..c2c73e685a --- /dev/null +++ b/Power-Monitoring-Calibration/index.html @@ -0,0 +1 @@ + Power Monitoring Calibration - Tasmota
      Skip to content

      Power Monitoring Calibration

      You need to calibrate your power monitoring device as correct measurements are influenced by hardware and timing differences.

      • Your power monitoring capable device flashed with Tasmota and configured with the correct module/template that supports power monitoring
      • An AC capable calibrated multi-meter
      • A known wattage load with a power factor as close to 1 as possible (e.g., a resistive load) for best results

      Note

      A resistive load device is any device which draws a constant amount of power. For example, an incandescent or halogen light bulb (best choice since their power draw is declared on them). An electric kettle, heater, or blow dryer are also options but you will also need a power meter since the power draw could vary.

      Danger

      Do not use switch mode driven devices such as LED lamps, computer equipment, or inductive/capacitive devices such as motors!

      • (optional) A calibrated power meter (a.k.a Kill-a-Watt) or AC multi-meter

      Setup~

      • Connect the load (e.g., a 60W incandescent light bulb) to your device
      • (optional) Plug your load into the Kill-a-Watt
      • Open two Tasmota web UI browser windows for your power monitoring device:
      • Click on Console in one browser window
      • Keep the other on the main page to view the Power telemetry data
      • Turn the power on to your device. Be sure to turn the output on so the load is powered on as well
      • Wait a few seconds for the readings to stabilize

      Calibration Procedure~

      1. Verify the Power reading in the web UI (optionally with the power meter as well) for the expected wattage. Adjust the power offset if needed (in Watts):
        PowerSet 60.0
        If you're using something other than a 60W bulb, enter your load's power rating

      2. Verify the Voltage reading. Adjust the voltage offset if needed (in Volts):
        VoltageSet <voltage>
        Replace <voltage> with your standard voltage or with reading on your multi-meter if you have one. Your voltage will vary depending on the electrical standards and your electrical grid

      3. Verify the Current reading by calculating current value (amperage) using this formula: P(W)/V(V)=I(A). Adjust the current offset if needed (in milliAmps (mA=A*1000)):
        CurrentSet <current>
        Replace <current> with your calculated value (in milliAmps)

      CurrentSet calculation:
      P/V=I 1000 * Watts/Volts = milliAmperes

      Example

      1000*(60.0/235.5) = 254.777

      1. Confirm the validity of your calibration process checking Power Factor from the web UI which should be as close as possible to 1.00. In theory resistive loads will always provide a power factor of 1.00. If that is not the case, we recommend you repeat the calibration process and make sure everything was done correctly.

      Fine Tuning~

      This procedure requires the use of a calibrated power meter or AC multi-meter.

      Commands CurrentCal, PowerCal and VoltageCal allow fine tuning of the power calibration.

      Repeat the procedure below for each of the readings: Current, Power, and Voltage using the corresponding calibration command (CurrentCal, PowerCal, and VoltageCal respectively). Take note that the offset ranges vary for each command.

      1. Check the reading using a multi-meter
      2. Compare it with the reading on the Tasmota web UI
      3. If there is an observed difference, change the offset value by issuing the calibration command in the Console (e.g., PowerCal 10000)
      4. Adjust the offset value up or down until the readings on the multi-meter and the web page are as close as possible

      The CurrentCal, PowerCal, VoltageCal commands accept values up to 32000. If the offset values you would like to specify are larger, you may have configured an incorrect power monitoring chip in the template. For example: if you specify the BL0937 (134) while you actually have an HLW8012 (133). Change the template to proper power monitoring chip to fix.

      Known Issues~

      Power monitoring chips such as HLW8032 (Blitzwolf SHP5) and CSE7766 (Sonoff S31, Sonoff POW R2) occasionally report invalid power measurements for load values below 5W. During this situation it sometimes reports a valid load. By setting SetOption39 to 128 (default) it must read at least 128 invalid power readings before reporting there is no load.

      To discard all loads below 6W simply set SetOption39 1 (0 will reset to default on next restart) so it will report no load below 6W.

      \ No newline at end of file diff --git a/PowerOnState/index.html b/PowerOnState/index.html new file mode 100644 index 0000000000..81a012eead --- /dev/null +++ b/PowerOnState/index.html @@ -0,0 +1 @@ + PowerOnState - Tasmota
      Skip to content

      PowerOnState

      PowerOnState Functionality~

      Command Description
      PowerOnState Control relay state after powering up the device.
      0 / OFF = keep relay(s) OFF after power up
      1 / ON = turn relay(s) ON after power up
      2 / TOGGLE = toggle relay(s) from last saved state
      3 = switch relay(s) to their last saved state (default)
      4 = turn relay(s) ON and disable further relay control
      5 = after a PulseTime period turn relay(s) ON (acts as inverted PulseTime mode)

      The PowerOnState device configuration parameter is applied when the device is initially powered up. It does not apply to device warm restarts.

      Tasmota tracks the relays' state in a masked variable. A set bit (1) means the corresponding relay is turned ON. The associated GPIO state will be high or low according to whether the relay is configured as Relay<x> or Relay<x>i. Every command for setting the relay state is "recorded" in the variable and saved to flash (depending on SetOption0). The setting of the relay GPIO is then executed.

      After a warm restart, the mask variable is re-initialised with the saved state from flash and the relay(s) set to that state. PowerOnState is not executed. During any a device restart, the relay power feedback state is scanned according to the setting of SetOption63. Scanning the relay state attempts to READ from GPIOs that are configured as relays, i.e., OUTPUTS! The result will not always be what is expected as it depends on how the device relays are wired to the GPIO. SetOption63 was introduced to make this scan optional. With SetOption63 set to 0 (the default), each GPIO assigned as a Relay<x> or Relay<x>i is scanned using 'digitalRead'. The mask variable will be updated with the detected values. The state of the relay(s) will not be changed. READING from an OUTPUT GPIO may result in the mask value being different from the state the relays are in. Thus, SetOption63 was introduced to disable the startup scan for devices where the scan leads to undefined results.

      SetOption63 is executed after PowerOnState or restart initialization.

      \ No newline at end of file diff --git a/Project-AM312-and-Sonoff-R2/index.html b/Project-AM312-and-Sonoff-R2/index.html new file mode 100644 index 0000000000..d4a52b6d53 --- /dev/null +++ b/Project-AM312-and-Sonoff-R2/index.html @@ -0,0 +1,6 @@ + Project AM312 and Sonoff R2 - Tasmota
      Skip to content

      Project AM312 and Sonoff R2

      This use case represents a method to use AM312 as a "wave hand toggle" (for under-cabinet kitchen LED). Please note that this solution isn't working in 100% (this sensor has a detection range of a few meters, to decrease the range you can remove the lens from the sensor but still it will pick up movement from 50 cm. You can create the Tasmota rule that will disable AM312 toggle action when the light is on and turn off the power after a few minutes. The gesture sensor APDS-9960 should work better for "hand-wave" toggle.

      Wiring for Sonoff Basic R2~

      As the R2 version doesn't have GPIO14 exposed you can use GPIO3 (RX) as the AM312 data pin. GPIO2 goes high during the boot (it would toggle the switch then).

      AM312 ESP8255 device
      VCC 3V3 or VCC
      VOUT GPIO3 (RX)
      GND GND

      Remember to remove the lens to lower the sensitivity of the sensor.

      Configuration~

      1. Go to IP of the device, next Configuration --> Configure Module --> set "GPIO3 Serial In" to "Switch1 (9)"
      2. Go to Console and type "SwitchMode 4" (detailed description of SwitchModes) to enable toggle switch type.
      3. Set rule to turn off light after X amount of seconds (mentioned workaround):
        rule1 on Switch1#State=2 do backlog Power1 1; RuleTimer1 180 endon on Rules#Timer=1 do backlog Power1 0 endon
        +rule1 1
        +

      Rule explanation: Switch1#State=2 - fire the event when switch1 is toggled, Power1 1 - turn on power, RuleTimer1 180 - set Timer1 to 180 seconds and start counting, Rules#Timer=1 - fire the event when Timer1 has stopped, Power1 0 - turn off power.

      This rule will turn off the light after 3 minutes, if the movement will be detected prior, the timer will be restarted and will count the time from the beginning.

      1. Instead of point 3, you can set below rules in order to ignore the second and next movements. It will just turn off the power after 3 minutes.
        rule1 on Switch1#State=2 do backlog Power1 1; RuleTimer1 180; Rule1 0; Rule2 1 endon
        +rule2 on Rules#Timer=1 do backlog Power1 0; Rule1 1; Rule2 0 endon on Switch1#State=2 do break
        +backlog rule1 1; rule2 0
        +

      Rules explanation: rule1 on Switch1#State=2 do backlog Power1 1; RuleTimer1 180; Rule1 0; Rule2 1 endon: Switch1#State=2 - fire the event when switch1 is toggled, Power1 1 - turn on power, RuleTimer1 180 - set Timer1 to 180 seconds and start counting, Rule1 0 - disable Rule1, Rule2 1 - enable Rule2.

      rule2 on Rules#Timer=1 do backlog Power1 0; Rule1 1; Rule2 0 endon on Switch1#State=2 do break: Rules#Timer=1 - fire the event when Timer1 has stopped Power1 0 - turn off power Rule1 1 - enable Rule1 Rule2 0 - disable Rule2 Switch1#State=2 do break - ignore toggling

      \ No newline at end of file diff --git a/Projector/index.html b/Projector/index.html new file mode 100644 index 0000000000..21756d63ff --- /dev/null +++ b/Projector/index.html @@ -0,0 +1,14 @@ + LCD/DLP Projector Control - Tasmota
      Skip to content

      LCD/DLP projector Serial Control~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #define USE_PROJECTOR_CTRL
      +// _Mandatory:_ choose the communication protocol for your projector. define only one of them:
      +#define USE_PROJECTOR_CTRL_NEC
      +#define USE_PROJECTOR_CTRL_OPTOMA
      +#define USE_PROJECTOR_CTRL_ACER
      +// _Optional:_ set the Relay that powers your device (default is 1)
      +#define PROJECTOR_CTRL_PWR_BY_RELAY 1  //the relay that powers the projector
      +// _Optional:_ set the baudrate to communicate to your device (default is 9600):
      +#define PROJECTOR_CTRL_SERIAL_BAUDRATE 9600
      +// _Optional:_ enable debugging messages:
      +#define DEBUG_PROJECTOR_CTRL
      +// _Optional:_ Add Serial to TCP Bridge to your build if you want to play with your projector's communication protocol over network connection. Useful for ASCII protocol testing. Needs a different GPIO configuration and is completely independent to this module:
      +#define USE_TCP_BRIDGE
      +

      Description~

      This driver simulates an additional relay in your Tasmota device. If you have N physical relays and you configure GPIO pin functions DLP Tx and DLP Rx you'll see relay (N+1) after reboot. The two GPIO pins will be used for serial communication with your LCD or DLP projector. The communication protocol is unique for each manufacturer (compile-time option). The driver polls the projector's state periodically and updates the fake relay state. When you toggle the fake relay, serial commands are sent to the projector to power it up or down. While the projector is running, the driver prevents to switch off the real relay that feeds the projector. This protects the lamp of the projector (needs to be cooled down before power is cut from the device).

      Supported Projectors~

      • NEC projectors - tested with NEC V300W
      • OPTOMA projectors - not tested yet / report your results!
      • Acer projectors - tested with Acer P1500 & H5360BD

      Fixes and definitions for further manufacturers should go to tasmota\xdrv_53_projector_ctrl.h

      Physical Connection~

      Connect your Tasmota GPIO pins (3.3V TTL level) to a MAX3232 interface (cheap items on internet sales). Such interface changes TTL signals to proper RS232 levels. There are 4 wires on TTL side (Vcc, GND, Rx and Tx) and 3 wires on RS232 side (GND, Tx and Rx). A wire jumper between pins 7(RTS) and 8(CTS) may be needed in DSUB9 connector going to projector.

      Unboxed

      Mounted

      V300W

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. GPIO1 to DLP Tx
      2. GPIO3 to DLP Rx

      Replace GPIO1/GPIO3 with your scenario. Use command Weblog 3 to see extended logging of serial communication in Console.

      Projector Configuration~

      Check your projector settings concerning Serial port. It must match Tasmota settings eg. 9600 8N1. Some models have "ID number" feature to allow several projectors in one room. The control commands in Tasmota contain ID 0. Please switch off the "ID" control completely or set the ID to 0.

      Since Acer H5360BD does not offer a simple RS2332 port setup is as follows: ESP01-RS232-AcerH5360

      \ No newline at end of file diff --git a/Python-HTTP-OTA-Server/index.html b/Python-HTTP-OTA-Server/index.html new file mode 100644 index 0000000000..1bfae1c8c3 --- /dev/null +++ b/Python-HTTP-OTA-Server/index.html @@ -0,0 +1,31 @@ + Python HTTP OTA Server - Tasmota
      Skip to content

      Python HTTP OTA Server

      Introduction~

      Tasmota firmware can be upgraded using 'Firmware Upgrade' option on the main menu and selecting an OTA URL to fetch (and flash) a new firmware on the device.

      To use this feature, firmware files need to be hosted on an HTTP web server (e.g., http://ota.tasmota.com/tasmota). Alternatively, users can to deploy a local HTTP server with Apache, Nginx, or other software solutions.

      Python makes available the Flask (micro)framework that can be used at different levels of complexity due to its modular architecture. A simple HTTP server (hello-world) can be deployed with only six lines of Python code.

      A Flask application to offer Tasmota firmware images for OTA upgrades is available as part of the available tools.

      Requirements~

      • Switching to superuser privileges and installing additional packages (and libraries) on a Linux/Windows box.
      • Python3 (follow instruction related to your operating system)
      • netifaces and Flask libraries - can be installed by 'pip' package manager:
        pip install netifaces flask
        +

      Instructions~

      Copy Tasmota firmware binary files in 'tools/fw' directory. A set of pre-built firmware binary files can be downloaded from the Tasmota repository.

      Configure your Tasmota device with your firmware server URL:

      Firmware Upgrade -> Upgrade by web server:
      +     http://<ip_address>:5000/tasmota.bin
      +

      or use the following command:

      Backlog OTAURL http://<ip_address>:5000/tasmota.bin; Upgrade 1
      +

      Usage~

      To start Python HTTP server:
      python fw-server.py -d <net_iface> (default: eth0)
      or
      python fw-server.py -i <ip_address>

      Example:
      python fw-server.py -d wlan0
      or
      python fw-server.py -i 192.168.1.1

      Note: On Windows it is advisable to use '-i' option because Windows uses UUID naming for network interfaces that are difficult to enter.

      Linux server:~

      If your MQTT broker is hosted on a local server, you may want your firmware web server used for Tasmota OTA updates to reside on the same server. Follow these steps to create it as a service:
      - Copy the python script on the Linux server:

      $ sudo su
      +# mkdir /srv/tasmota/fw_server/fw
      +# cd /srv/tasmota/fw_server
      +# wget https://github.com/arendst/Tasmota/tree/development/tools/fw_server/fw-server.py
      +
      - Create a new file named tasmota.service in /etc/systemd/system/ and paste these lines (replace XYZ by your username):
      [Unit]
      +Description=Local OTA server for Tasmota
      +Requires=network.target
      +After=network.target multi-user.target
      +
      +[Service]
      +User=XYZ
      +Type=idle
      +ExecStart=/usr/bin/python /srv/tasmota/fw_server/fw-server.py -d wlan0
      +Restart=on-failure
      +
      +[Install]
      +WantedBy=multi-user.target
      +
      - User rights:
      The files and directories have been created as root but this is not desirable. Invoke the following commands (replace the four instances of XYZ by your username):
      # chown -hR XYZ:XYZ /srv/tasmota
      +# chown XYZ:XYZ /etc/systemd/system/tasmota.service
      +
      - Run the service:
      # systemctl daemon-reload
      +# systemctl enable tasmota.service
      +# systemctl start tasmota.service
      +

      If the server is rebooted, the service will automatically restart.
      - Check that the service is active and running:

      # systemctl status tasmota.service
      +
      - Test the server:
      Copy new firmware files to the /srv/tasmota/fw_server/fw folder. Ensure that they have XYZ user rights in a similar fashion as shown above. You can use scp or a samba share. Copy the firmware files into the folder (firmware.bin in the example below).

      The service can be tested from any browser by issuing the address http://<ip_address>:5000/firmware.bin were <ip_address> is the address of the Linux server.

      • If the web server becomes unresponsive:
        After a power failure, your wlan0 IP may resolve to an invalid value like 169.254.5.153 because your LAN gateway was not ready when the web server restarted. If you notice that the OTA mechanism is broken, try to download the file from a browser. If you can't, then restart the service and check the status. Log in using a terminal session and enter:

        $ sudo su
        +# systemctl restart tasmota.service
        +# systemctl status tasmota.service
        +
        If you recognize the IP address of your server on the last line of the status output, the web server should be OK. You can confirm that your OTA web server is restored by trying to download a file using your web browser.

      • PlatformIO:
        If you want PlatformIO to be able to upload your compiled binaries to the local server, you'll still have to setup ssh with ssh-keygen in order to use the Tasmota script pio/sftp-uploader.py without a password.

      \ No newline at end of file diff --git a/RCWL-0516/index.html b/RCWL-0516/index.html new file mode 100644 index 0000000000..8b60667e13 --- /dev/null +++ b/RCWL-0516/index.html @@ -0,0 +1 @@ + RCWL-0516 microwave radar motion sensor - Tasmota
      Skip to content

      RCWL-0516 microwave radar motion sensor~

      module uses a “microwave Doppler radar” technique to detect moving objects. Its advantage over traditional PIRs is that it can detect presence through obstacles with a sensing distance of 3m average in real life conditions.

      Configuration~

      Wiring~

      RCWL-0516 ESP
      GND GND
      VIN 5V
      OUT GPIOx

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      • GPIOx to Switch1 (9)

      A configured RCWL-0516 will not appear in the web UI, only the console displays the status of Switch1 as POWER.

      Continue to use RCWL-0516 just as any other PIR in Tasmota.


      Buy from AliExpress
      Features of RCWL-0516
      RCWL-0516 Datasheet
      RCWL-0516 GitHub

      \ No newline at end of file diff --git a/RDM6300/index.html b/RDM6300/index.html new file mode 100644 index 0000000000..e691025427 --- /dev/null +++ b/RDM6300/index.html @@ -0,0 +1,8 @@ + RDM6300 RFID reader - Tasmota
      Skip to content

      RDM6300 RFID reader~

      This feature is included only in tasmota-sensors and tasmota32 binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_RDM6300 
      +#define USE_RDM6300         // Add support for RDM6300 125kHz RFID Reader (+0k8)
      +#endif
      +

      RDM6300 125KHz cardreader mini-module is designed for reading code from 125KHz card compatible read-only tags and read/write card.

      Warning~

      There seems to be an issue with some card readers!(the cheap ones?) These output some fake/ghost readings that look like valid card numbers and validate thru the XOR method but are not real. This is confirmed by multiple users. Please see details in issue #9952. There seem to not be issues yet with SeeedStudio Grove 125Khz Reader or RDM630

      Configuration~

      Sensor uses serial interface for communication.

      Wiring~

      RDM6300 ESP
      TX GPIOx
      RX GPIOy
      Pin3 unused
      GND GND
      VCC 5V

      Tasmota Settings~

      Update 12.10.2020: Seems there is a new driver and you can use RDM6300 RX instead of the SerBr Tx and SerBr Rx. This also seems to show the last card read in the management interface. As a note, the card number that comes from the SerBr was 12 chars and the RDM6300 one has only 8, being stripped by the first 2 and last 2 chars.

      In the Configuration -> Configure Module page assign:

      1. GPIOx to SerBr Tx (71)
      2. GPIOy to SerBr Rx (72)

      If the RFID Tag is held in front of the Antenna the reader starts sending the Tag ID. The Tag Id is prefaced with a 0x02 and the end is delimited with a 0x03. If the reader reads the tag multiple times you would get one line with multiple times the Tag ID. For this to separate you can add a SerialDelimiter of 0x03. This will split the result from the serial into several single mqtt messages. But (probably) all the messages wil be prepended with a binary 0x02.

      Set SerialDelimiter to 3

      SerialDelimiter 3
      +

      OpenHab~

      sonoff-rfid.items:

      // tele/sonoff-rfid-1/RESULT {"SSerialReceived":"01020304FAFA"}
      +String RFID_1      "RFID [%s]"
      +      {mqtt="<[broker:tele/sonoff-rfid-1/RESULT:state:JSONPATH($.SSerialReceived)]"}
      +

      Breakout Boards~

      \ No newline at end of file diff --git a/RF-Protocol/index.html b/RF-Protocol/index.html new file mode 100644 index 0000000000..0763471975 --- /dev/null +++ b/RF-Protocol/index.html @@ -0,0 +1,6 @@ + RF Communication - Tasmota
      Skip to content

      RF Communication

      This feature is included only in tasmota-sensors and tasmota32 binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_RC_SWITCH
      +#define USE_RC_SWITCH         // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram)
      +#endif
      +

      This guide does not apply to Sonoff RF Bridge specific commands

      Tasmota uses the rc-switch library to decode RF codes. Supported hardware depends on rc-switch library support only.

      Sending RF Codes~

      Send an RF control code as a decimal or hexadecimal string in a JSON payload. An inexpensive RF receiver such as a STX882 can be connected to a device running Tasmota. In order to send RF data you need to configure the connected GPIO as RFSend (105)

      Command Parameters
      RfSend <value> = code decimal or JSON. Data value is required and can be decimal or hexadecimal (using the 0x prefix), other values are optional.

      JSON
      {"Data":"<value>","Bits":<value>,"Protocol":<value>,"Pulse":<value>}
      "Data":"<value>" = hexadecimal code
      "Bits":<value> = required number of data bits (default = 24)
      "Protocol":<value> = protocol number (default = 1)
      "Repeat":<value> = repeat value (default = 10)
      "Pulse":<value> = pulse value (350 = default for protocol 1)
       e.g., RfSend {"Data":"0x7028DC","Bits":24,"Protocol":1,"Pulse":238}

      Decimal
      data, bits, protocol, repeat, pulse
       e.g., RfSend 7350492, 24, 1, 10, 238 or RfSend 0x7028DC, 24, 1, 10, 238

      If you send only the "Data" value in decimal or hexadecimal other values will be sent as default.

      Example

      RfSend 123456

      will be sent as {"Data":"0x1E240","Bits":24,"Protocol":1,"Pulse":351}

      When sending decimal formatted command you need to follow the pattern otherwise a part of the command will be ignored.

      Example

      RfSend 123456, 24, 1, 238 is missing the repeat value so the last number intended as pulse value won't be sent

      Receiving RF Codes~

      An inexpensive RF receiver such as a SRX882 can be connected to a device running Tasmota. Configure the GPIO connected to Data pin on the RF receiver as 'RFrecv (106)'.
      Once you have identified the protocols that you want to receive data on , you can enable only those protocols .
      The RfProtocol command will only work if you have set a pin function to rfrecv

      Command Parameters
      RfProtocol <value> <value> 0 .. 0x7FFFFFFFF or 'A' for All :: This Sets the Enabled Protocol Mask Value
      RfProtocol<idx> <value> <idx> 1 .. 35 <value> 0 or 1 :: This Disables or Enables a Specific Protocol

      Example

      RfProtocol 5
            will enable only protocols 1 & 3       stat/tasmota_D728A8/RESULT {"RfProtocol":"1,3"}
      RfProtocol27 1
            will enable protocol 27       stat/tasmota_D728A8/RESULT {"RfProtocol":"1,3,27"}
      RfProtocol 0
           stat/tasmota_D728A8/RESULT {"RfProtocol":"None Enabled"}
      RfProtocol a
           stat/tasmota_D728A8/RESULT {"RfProtocol":"1,2,3,4,5,6,7,8,9,10,11,12,
           13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35"}

      If you have an RF receiver configured, a message will be logged each time an RF code is seen. RF driver will try to decode it against all protocols supported by rc-switch library.

      When Tasmota receives an RF message, the data portion of the payload has the same format as the RfSend JSON parameters.

      "RfReceived":{"Data":"<hex-value>","Bits":<value>,"Protocol":<value>,"Pulse":<value>}
      +

      This JSON payload data can be used in a rule such as:

      ON RfReceived#Data=<hex-value> DO <command> ENDON
      +

      Examples:
      Sonoff RM433 Remote
      MQT: tele/tasmota/RESULT = {"Time":"2020-05-27T18:59:06","RfReceived":{"Data":"0x7028D2","Bits":24,"Protocol":1,"Pulse":238}}

      \ No newline at end of file diff --git a/RF-Transceiver/index.html b/RF-Transceiver/index.html new file mode 100644 index 0000000000..dafb2af4f7 --- /dev/null +++ b/RF-Transceiver/index.html @@ -0,0 +1,10 @@ + RF Transceiver - Tasmota
      Skip to content

      RF Transceiver

      RC Switch driver enables sending RF codes through an RF transmitter and receiving them through an RF receiver via rc-switch library.

      RF Transmitter~

      Not all transmitter modules will work with the rc-switch library. Some of officially supported modules by rc-switch are:

      • SC5262 / SC5272
      • HX2262 / HX2272
      • PT2262 / PT2272
      • EV1527 / RT1527 / FP1527 / HS1527
      • Intertechno outlets
      • HT6P20X

      This guide was created using STX882 RF transmitter which also works without issues.

      Wiring~

      RF ESP
      data GPIOx
      + 3.3v/5v
      - GND

      Tasmota~

      In the Configuration -> Configure Module page assign:

      • GPIOx to RFSend

      See RF commands for use.

      RF Receiver~

      RF receiver is used to capture RF codes. Those codes can be sent using RFSend or used as a rule trigger.

      Not all transmitter modules will have sufficient power or range for normal use

      This guide is using SRX882 RF receiver module with a helical antenna.

      Wiring~

      SRX882 ESP
      Data GPIOx
      VCC 5v
      GND GND
      CS 3v3 or 5v

      CS pin needs to be pulled high to put the module in active mode

      Tasmota~

      In the Configuration -> Configure Module page assign:

      • GPIOx to RFrecv (106)

      On a captured code RF receiver sends a tele/%topic%/RESULT JSON response visible in console:

      {
      +  "Time": "2019-01-01T00:00:00",
      +  "RfReceived": {
      +    "Data": "0x7028D5",
      +    "Bits": 24,
      +    "Protocol": 1,
      +    "Pulse": 238
      +  }
      +}
      +
      \ No newline at end of file diff --git a/Range-Extender/index.html b/Range-Extender/index.html new file mode 100644 index 0000000000..336ab0a8a9 --- /dev/null +++ b/Range-Extender/index.html @@ -0,0 +1,48 @@ + Wi-Fi Range Extender - Tasmota
      Skip to content

      Range Extender~

      This feature allows Tasmota to publish an AP with a dedicated SSID that is then routed to the core WiFi network.

      This feature is not included in precompiled binaries

      To use it you must compile your build

      Copy platformio_tasmota_cenv_sample.ini to platformio_tasmota_cenv.ini which already contains the following sections for ESP8266 and ESP32. Or create a section in your platformio_tasmota_cenv.ini such as the following for an ESP8266:

      [env:tasmota-rangeextender]
      +monitor_port = /dev/cu.SLAB*
      +upload_port  = /dev/cu.SLAB*
      +upload_speed = 921600
      +build_unflags               = ${esp_defaults.build_unflags}
      +build_flags                 = ${esp82xx_defaults.build_flags}
      +                              -D FIRMWARE_RANGE_EXTENDER
      +                              -D USE_WIFI_RANGE_EXTENDER                ; adds about 11k to flash size
      +                              -D USE_WIFI_RANGE_EXTENDER_NAPT           ; Optional, adds about 1k to flash size
      +                              -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH   ; required option to use this feature
      +

      For ESP32 (note this extends the tasmota32idf4 section included in the platformio_tasmota_cenv_sample.ini):

      [env:tasmota32-rangeextender]
      +extends                 = env:tasmota32idf4
      +build_flags             = ${env:tasmota32idf4.build_flags}
      +                          -D FIRMWARE_TASMOTA32
      +                          -D USE_WIFI_RANGE_EXTENDER
      +                          -D USE_WIFI_RANGE_EXTENDER_NAPT
      +

      Alternatively, add the following to user_config_override.h (however the PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH or tasmota32idf4 must still be set as per above):

      #ifndef USE_WIFI_RANGE_EXTENDER
      +#define USE_WIFI_RANGE_EXTENDER
      +#endif
      +// Optionally, to enable NAT/NAPT
      +#ifndef USE_WIFI_RANGE_EXTENDER_NAPT
      +#undef USE_WIFI_RANGE_EXTENDER_NAPT
      +#endif
      +

      Overview~

      If you have a Tasmota device at the edge of your WiFi coverage and require a bit more distance, this feature allows you to turn it into a routing WiFi node.

      Note that this should be considered low bandwidth as the ESP has to process every packet.

      Additionally, if desired, it can also NAT/NAPT traffic, removing the need for a route on your core router. See below for the pros and cons of each option.

      Routing (not NAPT)~

      Routing with Range Extender

      If using routing, you must setup a return route on your "core" router for each extender node. Each extender must also have a unique client side IP address range, and a static core side IP address so the core router knows where to send routes.

      Pros

      • Can access web interface remote devices
      • Lower overhead on the extender device
      • Slightly smaller flash foot print if you choose to disable NAPT

      Cons

      • More complex to setup, requiring return routes and static IP addressing

      An example setup using commands to match the above image to route traffic, with an AP name rangeextender and password securepassword would be:

      RgxSSId rangeextender
      +RgxPassword securepassword
      +RgxAddress 10.99.1.1
      +RgxSubnet 255.255.255.0
      +RgxState ON
      +

      Alternatively, it could also be set in your user_config_override.h with:

      #define WIFI_RGX_SSID "rangeextender"
      +#define WIFI_RGX_PASSWORD "securepassword"
      +#define WIFI_RGX_IP_ADDRESS "10.99.1.1"
      +#define WIFI_RGX_SUBNETMASK "255.255.255.0"
      +#define WIFI_RGX_STATE 1
      +

      NAPT defaults to off - however if you needed to disable it, run RgxNAPT OFF.

      With NAPT~

      NAPT with Range Extender

      If using NAPT, deployment is a lot easier as there is no need for a static IP address, or adding routes to your core router. Also, all extender nodes could have the same "remote" IP address range as these addresses never enter into the "core" network.

      Pros

      • Very simple to setup

      Cons

      • More overhead on the extender device
      • Access to the web interface of the remote devices needs port forwarding -> see RgxPort command

      An example setup using commands to match the above image with NAPT, with an AP name rangeextender and password securepassword would be:

      RgxSSId rangeextender
      +RgxPassword securepassword
      +RgxAddress 10.99.1.1
      +RgxSubnet 255.255.255.0
      +RgxState ON
      +RgxNAPT ON
      +

      Alternatively, it could also be set in your user_config_override.h with:

      #define WIFI_RGX_SSID "rangeextender"
      +#define WIFI_RGX_PASSWORD "securepassword"
      +#define WIFI_RGX_IP_ADDRESS "10.99.1.1"
      +#define WIFI_RGX_SUBNETMASK "255.255.255.0"
      +#define WIFI_RGX_STATE 1
      +#define WIFI_RGX_NAPT 1
      +

      To access the web page (which uses tcp port 80) of a client with mac 11:22:33:44:55:66 you execute the following command after the client is connected:

      RgxPort tcp,8080,112233445566,80
      +
      If the result is OK the range extender port 8080 is forwarded to port 80 of the client and the web page can be accessed with URL http://range-extender-name:8080

      The setting is not permanent and needs to be repeated each time the client (re)connects since its IP could have changed.

      So, if access to a device behind the NAT is required it is easiest to configure this device with a static IP in the range extender subnet. Then the RgxPort command can be used with that IP instead of the MAC. Since the IP is known, it is not required that the IP is already active at the time of the port forward. That way the RgxPort command could be used in a simple startup rule. Given the range extender subnet is the default 192.168.4.0/24, and the device has a static IP of 192.168.4.44 then this command would work:

      RgxPort tcp,8084,192.168.4.44,80
      +
      If the result is OK the range extender port 8084 is forwarded to port 80 of the client and the web page can be accessed with URL http://range-extender-name:8084 while the client is active.

      Commands~

      Command Parameters
      RgxSSId The SSID of the AP that Tasmota will present for devices to connect to
      RgxPassword The password for the above SSID
      RgxAddress The IP address on the AP side, the connection to the "core" network IP addressing remains the same (eg: DHCP)
      RgxSubnet The subnet mask for the AP side, it's recommended to use 255.255.255.0. The AP will automatically use addresses from this subnet to serve via DHCP to connecting devices
      RgxState Enable or disable the AP. Note that turning the AP off will cause a reboot
      RgxNAPT Only available if USE_WIFI_RANGE_EXTENDER_NAPT is enabled. Enable or disable NAPT. Note that turning off NAPT will cause the device to reboot
      RgxClients Show JSON list of currently connected clients with their MAC and IP. On ESP32 last RSSI is also shown
      RgxPort protocol,range_extender_port,[client_mac|client_ip],client_port: Forward a tcp or udp STA port to a port of an AP client
      \ No newline at end of file diff --git a/Rule-Cookbook/index.html b/Rule-Cookbook/index.html new file mode 100644 index 0000000000..44a9c6b460 --- /dev/null +++ b/Rule-Cookbook/index.html @@ -0,0 +1,11 @@ + + + + + + + + +Redirecting... + + diff --git a/Rules/index.html b/Rules/index.html new file mode 100644 index 0000000000..bffcf16f68 --- /dev/null +++ b/Rules/index.html @@ -0,0 +1,524 @@ + Rules - Tasmota
      Skip to content

      Rules

      Rules expand the functionality of Tasmota with user configurable flexible logic

      Tasmota provides a Rule feature heavily inspired by the ESPEasy implementation while maintaining a small memory footprint. Automation solutions can be implemented without having to add dedicated code or use external solutions.

      Rules perform actions based on triggers (e.g., switch state change, temperature threshold, events like system boot, a defined timer elapsing, custom defined events, etc.) They are stored in flash and therefore will survive a reboot.

      Note

      Most pre-compiled builds have the Rules feature enabled. If you are >compiling your own firmware, in order to use rules, include #define USE_RULES in user_config_override.h.

      List of Rules Commands

      Rule Syntax~

      Rule definition syntax

      ON <trigger> DO <command> [ENDON | BREAK]
      +
      • ON - marks the beginning of a rule
      • <trigger> - what condition needs to occur for the rule to trigger
      • DO - statement marking end of trigger and beginning of command part
      • <command> - command that is executed if the <trigger> condition is met
      • ENDON - marks the end of a rule. It can be followed by another rule.
      • BREAK - marks the end of a rule. BREAK will stop the execution of the remaining rules that follow this rule within the rule set. If a rule that ends with BREAK is triggered, the following rules in that rule set will not be executed. This allows the rules to somewhat simulate an "IF/ELSE" statement.

      Rule sets are defined by using the Rule<x> command. After defining a rule set, you have to enable it (turn it on) using Rule<x> 1. Similarly you can disable the rule set using Rule<x> 0.

      Note

      If bootloops are detected all rules will be disabled as a precaution. See SetOption36.

      There are three separate rule sets called Rule1, Rule2 and Rule3. Each rule set can contain many rules. The number of rules that can fit in a rule set varies, but expect at least 1000 characters available per set. Additionally, rules are dynamically compressed, meaning they will be compressed automatically when doing so is required, but left uncompressed until that point.

      Whenever a rule set is enabled all the rules in it will be active. If the character count of the rules in one set actually exceeds the limit, start using the next rule set. If you have a long list of rules, verify the rules have all fit by inspecting the resulting log.

      Rules inside a rule set Rule<x> are concatenated and entered as a single statement.

      Rule<x> ON <trigger1> DO <command> ENDON ON <trigger2> DO <command> ENDON ...
      +

      Spaces after ON, around DO, and before ENDON or BREAK are mandatory. A rule is not case sensitive.

      Rule Trigger~

      Rule trigger names are derived from the JSON message displayed in the console. Each JSON level (all values enclosed in {...}) is separated in the trigger with a #. Top level JSON fields are referenced without any #, except when the JSON has only one field, where you need #Data.

      A rule trigger can consist of:

      • [TriggerName]#[ValueName]
      • [TriggerName]#[ValueName][comparison][value]
      • [SensorName]#[ValueName]
      • [SensorName]#[ValueName][comparison][value]
      • Tele-[SensorName]#[ValueName]
      • [TriggerName1]#[TriggerName2]#[ValueName]
      • [TriggerName1]#?#[ValueName]
      • [ValueName]
      • [ValueName#Data]

      Use ? as a wildcard for a single trigger level. Rule will trigger on [TriggerName]#?#[Value] where ? is any value.

      Example

      Rule with a trigger of ZBReceived#?#Power=0 will trigger on {"ZBReceived":{"0x4773":{"Power":0}}} and on {"ZBReceived":{"aqara_switch":{"Power":0}}} both.

      Note

      Same trigger may be used in more than one rule. This may be required for some cases of using IF/ELSE since an IF statement cannot be used within a Backlog.

      Rule Trigger Comparison Operators~

      Operator Function
      = equal to (used for string comparison)
      == equal to (used for numerical comparison)
      > greater than
      < lesser than
      != number not equal to (for string see below)
      >= greater than or equal to
      <= lesser than or equal to
      $< string starts with
      $> string ends with
      $| string contains
      $! string is not equal to
      $^ string do not contains
      | used for modulo operation with remainder = 0 (exact division)

      Examples of Available Triggers~

      This is just a sampling of available triggers to showcase what is possible and not a definitive list

      Trigger When it occurs
      Analog#A0div10 when the A0 input changes by more than 1% it provides a value between 0 and 100
      Button2#State when a button changes state:
      0 = OFF
      1 = ON
      2 = TOGGLE
      3 = HOLD
      Clock#Timer=3 when global Timer3 is activated
      Dimmer#Boot occurs after Tasmota starts
      Dimmer#State when the value for Dimmer is changed
      Eth#Connected when Ethernet is connected (ESP32 only)
      Eth#Disconnected when Ethernet is disconnected (ESP32 only)
      Event#eventName when command Event eventName is executed. You can define your own event values and trigger them with the Event command. An event with a = will provide a %value% to use in the execution part of the rule. Example: Command Event speed=2 in rule trigger on event#speed will have the %value% of 2.
      FanSpeed#Data=3 when the fan speed is set to 3
      Mem<x>#State when the value for Mem<x> is changed
      Http#Initialized
      Mqtt#Connected when MQTT is connected
      Mqtt#Disconnected when MQTT is disconnected
      Power1#Boot Relay1 state before Wi-Fi and MQTT are connected and before Time sync but after PowerOnState is executed. Power#Boot triggers before System#Boot.
      This trigger's value will be the last state of Relay1 if PowerOnState is set to its default value (3).
      Power1#State when a power output is changed
      use Power1#state=0 and Power1#state=1 for comparison, not =off or =on
      Power2 for Relay2, etc.
      Rotary1#Pos1 when rotary encoder change. See Use a rotary encoder.
      Rules#Timer=<x> when countdown RuleTimer<x> expires (x = 1..8).
      Switch1#Boot occurs after Tasmota starts before it is initializated.
      Switch1#State when a switch changes to state. Will not trigger if SwitchTopic is set.
      use Switch1#state=0 and Switch1#state=1 for comparison, not =off or =on
      0 = OFF
      1 = ON
      2 = TOGGLE
      3 = HOLD (SwitchTopic 0 must be set for this to trigger)
      4 = INC_DEC (increment or decrement dimmer)
      5 = INV (change from increment to decrement dimmer and vice versa)
      6 = CLEAR (button released for the time set with SetOption32)
      System#Boot occurs once after Tasmota is fully initialized (after the INFO1, INFO2 and INFO3 console messages). System#Boot triggers after Wi-Fi and MQTT (if enabled) are connected. If you need a trigger prior to every service being initialized, use Power1#Boot
      System#Init occurs once after restart before Wi-Fi and MQTT are initialized
      System#Save executed just before a planned restart
      Time#Initialized once when NTP is initialized and time is in sync
      Time#Minute every minute
      Time#Minute|5 every five minutes
      Time#Minute=241 every day once at 04:01 (241 minutes after midnight)
      Time#Set every hour when NTP makes time in sync
      Var<x>#State when the value for Var<x> is changed (triggers whenever a value is written to Var<x> even if it's the same value)
      Wifi#Connected when Wi-Fi is connected
      Wifi#Disconnected when Wi-Fi is disconnected
      Tele-Heap when a teleperiod message is sent with available heap memory (example of top-level JSON)
      Tele-Wifi#AP when a teleperiod message is sent with the number of the used AP
      Tele-Wifi#Ssid when a teleperiod message is sent with the name of the used AP
      Tele-Wifi#Bssid when a teleperiod message is sent with the name of the bSSID
      Tele-Wifi#Channel when a teleperiod message is sent with the number of the wifi channel used
      Tele-Wifi#RSSI when a teleperiod message is sent with the RSSI LEVEL
      Tele-Wifi#LinkCount when a teleperiod message is sent with the number of wifi disconnections
      Tele-Wifi#Downtime when a teleperiod message is sent with the total seconds of wifi disconnections

      Every command with a JSON payload response has an associated rule trigger, with the exception of Power#Data and Switch, which are superseded by by the Power#State and Switch#State trigger.

      Trigger When it occurs
      <command>#Data A one level JSON payload such as {"Command":"value"}. For example, for {"Fanspeed":3}, the trigger isFanspeed#Data.
      <command>#level1#level2#levelN A multi-level JSON payload such as {"TriggerLevel1":{"TriggerLevel2":{"ValueName":"value"}}} does NOT have the #Data trigger. Instead, the trigger for these responses is TriggerLevel1#TriggerLevel2#ValueName.

      When the JSON payload response is produced by a command executed by a rule i.e. on time=120 do status 8 endon, the StatusSNS#Data trigger will not fire unless the command is wrapped in backlog i.e. on time=120 do backlog status 8 endon

      Example

      For {"PulseTime2":{"Set":0,"Remaining":0}}, the triggers are PulseTime2#Set and PulseTime2#Remaining.|

      For a 3 level JSON message such as {"ZbReceived":{"test_switch":{"Device":"0x0C94","Power":1,"Endpoint":8,"LinkQuality":70}}} one possible trigger is ZbReceived#test_switch#Power or another ZbReceived#test_switch#LinkQuality

      Connected sensors can be a trigger in the form as they are represented in the TelePeriod and Status 8 JSON payloads.

      Trigger When it occurs
      DS18B20#Temperature whenever the temperature of sensor DS18B20 updates (also unchanged)
      DS18B20#Temperature<20 whenever the temperature of sensor DS18B20 is below 20 degrees
      BME280#Humidity==55.5 whenever the humidity of sensor BEM280 equals 55.5%
      INA219#Current>0.100 whenever the current drawn is more than 0.1A
      Energy#Power>100 whenever the power used is more than 100W

      When the payload consists of an array of data eg: ENERGY":{Current":[1.320,2.100]}

      Trigger When it occurs
      Energy#Current[N] N = Number of the field. 1 for the first 1.320, 2 for the second 2.100 etc.
      Energy#Current[1]>1.000 whenever the first value of Energy#Current is higher than 1.000.

      To trigger only at TelePeriod time, prefix the sensor with the word Tele-.

      Trigger When it occurs
      Tele-AM2301#Temperature sensor AM2301 Temperature value when the TelePeriod JSON payload is output

      Hardware and software serial interface, RF, IR and TuyaMCU are also supported based on their JSON status message:

      Trigger When it occurs
      TuyaReceived#Data=<hex_string> whenever <hex_string> is received with TuyaMCU component
      SerialReceived#Data=<string> whenever <string> is received via hardware serial
      SSerialReceived#Data=<string> whenever <string> is received via software serial
      IrReceived#Data=801 whenever an IR signal for a RC5 remote control button 1 is received
      IrReceived#Data=0x00FF9867 whenever an IR signal with hex code 0x00FF9867 is received
      RfReceived#RfKey=4 whenever the RF Bridge receives a recognized RfKey 4 signal
      RfReceived#Data=0xE8329E whenever an RF signal with hex code 0xE8329E is received

      Rule Command~

      A rule command can be any command listed in the Commands list. The command's <parameter> can be replaced with %value% which will use the value of the trigger.

      ON Switch1#State DO Power %value% ENDON

      To accomplish a rule with one trigger but several commands, you need to use Backlog:

      ON <trigger> DO Backlog <command1>; <command2>; <command3> ENDON

      Appending new rule onto an existing rule set
      Use the + character to append a new rule to the rule set. For example:

          Existing Rule1: ON Rules#Timer=1 DO Mem2 %time% ENDON

          Rule to append: ON Button1#state DO POWER TOGGLE ENDON

          Command: Rule1 + ON button1#state DO POWER TOGGLE ENDON

          Resulting in

      Rule1 ON Rules#Timer=1 DO Mem2 %time% ENDON ON Button1#state DO POWER TOGGLE ENDON
      +

      You can repeate the same trigger in rules.

      Rule
      +  ON Power2#state=1 DO Power1 1 ENDON
      +  ON Power2#state=1 DO RuleTimer1 100 ENDON
      +

      Rule Variables~

      There are thirty-two (32) available variables (single precision reals) in Tasmota: Var1..Var16 and Mem1..Mem16. They provide a means to store the trigger %value% to be used in any rule.
      All Var will be empty strings when the program starts. The value of all Mem persists after a reboot.

      The value of a Var<x> and Mem<x> can be:

      • any number
      • any text
      • %var1% to %var16%
      • %mem1% to %mem16%
      • %color%
      • %deviceid%
      • %macaddr%
      • %sunrise%
      • %sunset%
      • %time%
      • %timer1% to %timer16%
      • %timestamp%
      • %topic%
      • %uptime%
      • %utctime%
      • %zbdevice%
      • %zbgroup%
      • %zbcluster%
      • %zbendpoint%

      To set the value for Var<x> and Mem<x> use the command

      • Var<x> <value>
      • Mem<x> <value>

      The <value> can also be the value of the trigger of the rule.

      • Set Var2 to the temperature of the AM2301 sensor - ON AM2301#Temperature DO Var2 %value% ENDON
      • Set Var4 to Var2's value - ON Event#temp DO Var4 %Var2% ENDON
      • Set Mem2 to the current time (minutes elapsed since midnight) - ON Rules#Timer=1 DO Mem2 %time% ENDON
      • After a Wi-Fi reconnect event, publish a payload containing timestamps of when Wi-Fi was disconnected in From: and when Wi-Fi re-connected in To: to stat/topic/BLACKOUT.
      Rule1
      +  ON wifi#disconnected DO Var1 %timestamp% ENDON
      +  ON wifi#connected DO Var2 %timestamp% ENDON
      +  ON mqtt#connected DO Publish stat/topic/BLACKOUT {"From":"%Var1%","To":"%Var2%"} ENDON
      +

      Delete rule~

      To clear / delete use double quote(s):

      Rule1 "
      +

      Conditional Rules~

      This feature is not included in precompiled binaries.

      To use it you must compile your build. Add the following to user_config_override.h:

      #define USE_EXPRESSION         // Add support for expression evaluation in rules (+3k2 code, +64 bytes mem)  
      +#define SUPPORT_IF_STATEMENT   // Add support for IF statement in rules (+4k2 code, -332 bytes mem)  
      +


      Major features~

      • Support IF, ELSEIF, ELSE
      • Support for <comparison> and <logical expression> as condition
      • Support for executing multiple commands
      • Available free RAM is the only limit for logical operators and parenthesis.

      Grammar~

      <if-statement>

      • IF (<logical-expression>) <statement-list> {ELSEIF (<logical-expression>) <statement-list>} [ELSE <statement-list>] ENDIF

      (<logical-expression>)
      Parentheses must enclose the expression. They can also be used to explicitly control the order of evaluation.

      • <comparison-expression>
      • ( <comparison-expression> | <logical-expression> ) {{AND | OR} <logical-expression>}
      • ( <logical-expression> ) {AND | OR} <logical expression>}

      <comparison-expression>

      • <expression> {= | < | > | | | == | <= | >= | !=} <expression>

      <statement-list>

      • <statement> {; <statement>}

      <statement>

      • {<Tasmota-command> | <if-statement>}

      Syntax~

      IF statement supports 3 formats:

      • IF (<logical-expression>) <statement-list> ENDIF
      • IF (<logical-expression>) <statement-list> ELSE <statement-list> ENDIF
      • IF (<logical-expression>) <statement-list> [ELSEIF (<logical-expression>) <statement-list> ] ELSE <statement-list> ENDIF

      When the <if-statement> directly follows the trigger the standard 'Do' syntax applies, however, it is not necessary to use 'Backlog' within the chain Rule1 ON Power1#State DO IF (%value%==1) Backlog Power2 1;Power3 1 ENDIF ENDON is permitted Rule1 ON Power1#State DO IF (%value%==1) Power2 1;Power3 1 ENDIF ENDON is also permitted

      When the <if-statement> is preceded by other Tasmota commands you should use Backlog along with Do , e.g.
      Rule1 ON ENERGY#Current>10 DO Backlog Power1 0; IF (%var1%==1) Power1 1 ENDIF;Power 2 0;Power3 1 ENDON and not Rule1 ON ENERGY#Current>10 DO Power1 0; IF (%var1%==1) Power1 1 ENDIF ENDON or Rule1 ON ENERGY#Current>10 Backlog Power1 0; IF (%var1%==1) Power1 1 ENDIF ENDON

      (<logical-expression>) example: (VAR1>=10)
      - Multiple comparison expressions with logical operator AND or OR between them. AND has higher priority than OR. For example:
      ((UPTIME>100) AND (MEM1==1) OR (MEM2==1))
      - Parenthesis can be used to change the priority of the logical expression evaluation. For example:
      ((UPTIME>100) AND ((MEM1==1) OR (MEM2==1)))

      • Following variables can be used in <condition>:
      Symbol Description
      VAR<x> variable (<x> = 1..MAX_RULE_VARS, e.g., VAR2)
      MEM<x> persistent variable (<x> = 1..MAX_RULE_MEMS, e.g., MEM3)
      TIME minutes past midnight
      UPTIME uptime minutes
      UTCTIME UTC time, UNIX timestamp, seconds since 01/01/1970
      LOCALTIME local time, UNIX timestamp
      SUNRISE current sunrise time (minutes past midnight)
      SUNSET current sunset time (minutes past midnight)
      COLOR current color

      <statement-list>
      - A Tasmota command (e.g.,LedPower on)
      - Another IF statement (IF ... ENDIF)
      - Multiple Tasmota commands or IF statements separated by ;. For example:
      Power1 off; LedPower on; IF (Mem1==0) Var1 Var1+1; Mem1 1 ENDIF; Delay 10; Power1 on
      Backlog is implied and is not required (saves rule set buffer space).

      But not like this: Power1 off; LedPower on; IF (Mem1==0) Var1 Var1+1; Mem1 1 ENDIF; Delay 10; Power1 on

      You should split it in two lines like: ON Power2#state=1 DO Power1 off; LedPower on; ENDON ON Power2#state=1 DO IF (Mem1==0) Var1 Var1+1; Mem1 1 ENDIF; Delay 10; Power1 on ENDON

      Example

      Rule used to control pressure cooker with a Sonoff S31. Once it is finished cooking, shut off the power immediately.

      Rule1
      + ON system#boot DO var1 0 ENDON
      + ON energy#power>100 DO if (var1!=1) ruletimer1 0;var1 1 endif ENDON
      + ON tele-energy#power<50 DO if (var1==1) var1 2;ruletimer1 600 endif ENDON
      + ON rules#timer=1 DO Backlog var1 0;power off ENDON  
      +

      Expressions in Rules~

      This feature is not included in precompiled binaries.

      To use it you must compile your build. Add the following to user_config_override.h:

      #define USE_EXPRESSION         // Add support for expression evaluation in rules (+3k2 code, +64 bytes mem)  
      +#define SUPPORT_IF_STATEMENT   // Add support for IF statement in rules (+4k2 code, -332 bytes mem)  
      +


      Beginning with Tasmota version 6.4.1.14, an optional feature for using mathematical expressions in rules was introduced.

      Supported Commands~

      Once the feature is enabled, the use of expressions is supported in the following commands:

      Syntax~

      Expressions can use of the following operators. They are listed by the order of operations priority, from higher to lower.

      • ( ) (parentheses can be used to explicitly control the order of operations)
      • ^ (power)
      • % (modulo, division by zero returns modulo "0")
      • * and / (multiplication and division; division by zero returns "0")
      • + and - (addition and subtraction)

      Example

      • 1+2*2 results in 5.0 as the multiplication is done first due to its higher priority
      • (1+2)*2 results in 6.0

      In addition to numeric constants, the following symbolic values can be used:

      Symbol Description
      VAR<x> variable (<x> = 1..MAX_RULE_VARS, e.g., VAR2)
      MEM<x> persistent variable (<x> = 1..MAX_RULE_MEMS, e.g., MEM3)
      TIME minutes past midnight
      UPTIME uptime minutes
      UTCTIME UTC time, UNIX timestamp, seconds since 01/01/1970
      LOCALTIME local time, UNIX timestamp
      SUNRISE current sunrise time (minutes past midnight)
      SUNSET current sunset time (minutes past midnight)
      COLOR current color

      Example

      Mem1=((0.5*Var1)+10)*0.7

      To use expressions in the Var, Mem and RuleTimer commands, an equal sign (=) character has to be used after the command. If not, the traditional syntax interpretation is used.

      Statement Var1 Result
      Var1=42 42
      Var1 1+1 "1+1" (the literal string)
      Var1=1+1 2
      Var1=sunset-sunrise duration of daylight in minutes

      Rule Cookbook~

      Long press on a switch~

      This example is for GPIOs defined as switches not buttons

      Activate long press action with Switchmode 5 and shorten long press time to 2 seconds (Setoption32 20).

      Backlog SwitchMode 5; SetOption32 20
      +Rule ON switch1#state=3 DO publish cmnd/tasmota02/POWER 2 ENDON
      +

      Long pressing on switch1 sends POWER 2 (toggle action) command to the tasmota02 device

      Notice we use Rule which edits Rule1 rule set. They can be used interchangeably. If your rule does not trigger there could some constraints, in this case if SwitchTopic has a value it will override rules for switches and will need to be disabled: SwitchTopic 0.


      Send MQTT message on button press~

      When a button is pressed the user has the possibility to send a MQTT message based on FullTopic and ButtonTopic. This MQTT message is going to be received by the MQTT broker and if there is any other device(s) subscribed to that Topic, it will receive also that message. So this approach can be used for sending messages/commands to MQTT Broker to Home Automation System, and/or sending messages/commands to MQTT Broker to other device(s).

      A problem with this solution is that on a Sonoff 4CH all four buttons will be sending the same MQTT topic using only a different Power index number like cmnd/ButtonTopic/Power3 toggle.

      By using a rule a single button can send any MQTT message allowing for more flexibility.

      Disable ButtonTopic as it overrides rules for buttons: ButtonTopic 0

      Rule~

      Rule1
      +  ON button1#state DO publish cmnd/ring2/power %value% ENDON
      +  ON button2#state DO publish cmnd/strip1/power %value% ENDON
      +

      You will need to enable this rule if it's the first time you've used rule with Rule1 1

      Result~

      When Button1 is pressed the rule kicks in and sends a MQTT message substituting variable %value% with the button state, f.e cmnd/ring2/Power 2. When Button2 is pressed an MQTT message cmnd/strip1/Power 2 will be sent.


      Usage of one-shot (once)~

      The rule command once option provides the possibility to trigger only once ON a slow change while the change is still within the bounds of the test.

      Rule
      +  ON ENERGY#Current>0.100 DO publish tool/tablesaw/power 1 ENDON
      +  ON ENERGY#Current<0.100 DO publish tool/tablesaw/power 0 ENDON
      +

      This creates a rule to publish MQTT commands whenever a Sonoff POW has current passing through it. Used as is, it will publish MQTT commands repeatedly, over and over, while current is >0.100 ... but, executing another command:

      Rule 5

      Now the MQTT message will be sent once, and only once, while the condition is met. This is perfect for thermostat on/off depending on temperature, bathroom extractor fan on/off depending on humidity, workshop dust collector on/off depending on whether some dust-producing machine is running.

      It meets the 'hard thermostat' requests that have been common.

      Use a potentiometer~

      Connecting a potentiometer to the Analog A0 input and a rule can be used to control the dimmer state of any device.

      Hardware - Wemos D1 mini - Potentiometer of 2k2 connected to Gnd, A0 and 3V3 - WS2812 LED

      Rule ON analog#a0div10 DO dimmer %value% ENDON
      +

      Result~

      Turning the potentiometer the voltage on the analog input will change resulting in a value change of 0 (Off) to 100 for the trigger. Using this value to control the dimmer of the WS2812 will control the brightness of the led(s)

      Rule ON analog#a0div10 DO publish cmnd/grouplight/dimmer %value% ENDON
      +

      Result This time all lights configured with GroupTopic grouplight will change their brightness according to the potentiometer position.

      NOTE: You might want to execute command SaveData 2 to reduce flash writes ;-)

      Use a rotary encoder~

      You can capture in rules the value of a rotary encoder connected to 2 GPIOs configured as Rotary_a|<n> and Rotary_b|<n>. Optionally the button of the rotary encoder can be connected to another GPIO configured as Button|<n>. <n> must be the same to allow the encoder to manage 2 absolute counters from the same rotary encoder.

      To get triggers from the rotary encoder into rules, you must enable SetOption98 1. The rotary encoder <n> provides a JSON in the form of {'Rotary<n>': {'Pos1': value, 'Pos2': value}}. You can use the following rules triggers:

      SetOption98 1
      +Rule1
      +  ON Rotary1#Pos1 DO something_with %value% ENDON
      +  ON Rotary1#Pos2 DO something_with %value% ENDON
      +

      Result~

      Pos1 is changed when the rotary encoder is turned while button is not pressed. Pos2 is changed while button is pressed. Both Pos1 and Pos2 are published whatever is the button position, so both trig at the same time.

      The button will still have its default action (such as toggling power). If you want to avoid that, you need to capture the button into a dummy rule such as ON Button1#state DO Delay 0 ENDON.

      The step range of the rotary encoder can be selected using SetOption43, and the default is hardcoded in #define ROTARY_MAX_STEPS 10. To change the default range, set it in your user_config_override.h and recompile.


      Use Zigbee to control Tasmota devices~

      This setup uses a Zigbee gateway with an Ikea remote switch paired.

      Ikea switch's name was set with ZbName to make it more user friendly.

      Rule~

      Rule1 on zbreceived#ikea_switch#power=1 do publish cmnd/backyard/POWER TOGGLE endon on zbreceived#ikea_switch#power=0 do publish cmnd/hall_light/POWER TOGGLE endon
      +

      Result~

      Pressing I on the Ikea switch will toggle backyard device and pressing O toggles hall_light device.


      Button single press, double press and hold~

      This example show how to assign different behavior to a button other than Button1. Button1 has special multi-press behaviors associated with it (see Note in Multi-Press Functions), examples 1 and 2 cannot be applied to Button1.

      1st example:~

      [assuming Button2 (or >2) and Setoption73 0]

      • single press: Toggle Power2 (or >2)
      • double press: send a mqtt message
      • hold 2 secs: send a different mqtt message
      Backlog ButtonTopic 0; SetOption1 1; SetOption11 0; SetOption32 20
      +Rule1
      +  ON button2#state=3 DO publish cmnd/topicHOLD/power2 2 ENDON
      +  ON button2#state=2 DO publish cmnd/topicDOUBLEPRESS/power2 2 ENDON 
      +Rule1 1
      +

      2nd example with Setoption11 1:~

      [assuming Button2 (or >2) and Setoption73 0]

      • single press: send MQTT message
      • double press: Toggle Power2 (or >2) (SetOption11 swaps single and double press)
      • hold 2 secs: send another mqtt message
      Backlog ButtonTopic 0; SetOption1 1; SetOption11 1; SetOption32 20  
      +Rule1
      +  ON button2#state=3 DO publish cmnd/topicHOLD/power2 2 ENDON
      +  ON button2#state=2 DO publish cmnd/topicSINGLEPRESS/power2 2 ENDON 
      +Rule1 1
      +

      3rd example for Button1:~

      For assigning different actions to multi-press on Button1, it is mandatory to detach buttons from their default function using SetOption73 1. With SetOption73 1 buttons only publish a MQTT message (stat/tasmota/BUTTON<x> = {"ACTION":"xxxx"}). To re-assign a specific action, rules must be used like below:

      • single press: Toggle Power1
      • double press: send a mqtt message
      • hold 2 secs: send a different mqtt message
      Backlog ButtonTopic 0;  SetOption73 1; SetOption32 20
      +Rule1
      +  ON button1#state=10 DO power1 2 ENDON
      +  ON button1#state=3 DO publish cmnd/topicHOLD/power 2 ENDON
      +  ON button1#state=11 DO publish cmnd/topicDOUBLEPRESS/power 2 ENDON
      +Rule1 1
      +

      Note

      SetOption73 1 detaches ALL buttons. If you have more than 1 button, you must create rules for each buttons where you want an action (other than publishing stat/tasmota/BUTTON<x> = {"ACTION":"xxxx"})


      Disable switch single press and use long press~

      SetOption11 0

      Switches do not have double press feature

      [assuming a connected pushbutton configured as Switch1]

      • single press: Does nothing (empty Delay commands)
      • hold 2 secs: Toggle Power1
      Backlog SwitchTopic1 0; SwitchMode1 5; SetOption32 20  
      +
      Rule1
      +  ON Switch1#State=3 DO Power1 2 ENDON
      +  ON Switch1#State=2 DO Delay ENDON  
      +

      Rule1 1


      Execute several commands when a Timer expires~

      The default Timer1..16 functionality allows for controlling one output to either off, on, toggle or blink. When rules are enabled the blink option will be replaced by rule functionality allowing much more flexibility.

      Configure timer5 for rule execution when activated:

      Timer5 {"Enable":1,"Mode":0,"Time":"16:00","Days":"1111111","Repeat":1,"Action":3}
      +

      Rule~

      Rule1 ON clock#Timer=5 DO Backlog Power2 on; Power1 off; Power3 2 ENDON
      +

      Result~

      When the timer expires the rule kicks in and set Power1 to OFF, Power2 to ON and Power3 TOGGLE.

      If you want to have blink functionality define a rule like ON clock#Timer=5 DO power 3 ENDON


      Setting variables~

      Demonstrate the use of variables. Make sure to execute commands Rule 4(Disable one-shot detection) first when trying the following example.

      Set a variable

      Rule ON event#setvar1 DO var1 %value% ENDON
      +

      Command: event setvar1=1

      View a variable

      rule ON event#getvar1 DO var1 ENDON
      +

      Command: event getvar1

      • Toggle a variable
      Rule
      +  ON event#togglevar1 DO event toggling1=%var1% ENDON
      +  ON event#toggling1<1 DO event setvar1=1 ENDON
      +  ON event#toggling1>0 DO event setvar1=0 ENDON
      +  ON event#setvar1 DO var1 %value% ENDON
      +

      Command: event togglevar1

      Show Messages:

      Rule ON event#message DO publish stat/[topic]/log %value% ENDON
      +

      Command: event message=INIT

      All event commands can be executed from:

      • console: event anyname=number
      • mqtt: cmnd/[topic]/event anyname=number

      Everything together:

      Rule1 
      +  ON event#togglevar1 DO event toggling1=%var1% ENDON 
      +  ON event#toggling1<1 DO event setvar1=1 ENDON 
      +  ON event#toggling1>0 DO event setvar1=0 ENDON 
      +  ON event#setvar1 DO var1 %value% ENDON 
      +  ON event#getvar1 DO var1 ENDON 
      +  ON event#message DO publish stat/mqttTopic/log %value% ENDON
      +

      The following won't work:

      Rule1 ON event#setvar1 DO Backlog var1 %value%; Power1 %var1% ENDON

      At least not as you probably would expect. The var1 value used by the Power1 command will be the value present before the Backlog command is executed. This is so, because the rule will replace %var1% BEFORE the Backlog commands are put in the Backlog command stream.


      Time-delayed Auto-off Switch~

      Rule~

      Rule1
      +  ON button1#state DO Backlog Power1 %value%; RuleTimer1 600 ENDON
      +  ON Rules#Timer=1 DO Power1 off ENDON
      +

      Result~

      on button1#state do Backlog Power1 %value%;
      On Button press the Light will toggle on/off

      RuleTimer1 600 ENDON
      Additionally RuleTimer1 will begin to countdown 10 minutes

      ON Rules#Timer=1 DO Power1 off ENDON
      After the RuleTimer1 expires the light will be turned off (if you forgot to turn it off)


      Time-delay After Switch Off~

      Rule~

      Backlog switchmode1 1; rule1 1
      +
      Rule1 ON switch1#state=1 DO Backlog Power1 on; ruletimer1 0 ENDON
      +ON switch1#state=0 DO ruletimer1 300 ENDON
      +ON rules#timer=1 DO Power1 0 ENDON
      +

      Result~

      ruletimer1 300 sets a 5 minute timer. After that time, fan will be switched off. If during the defined 5 minutes (or in general - when timer is counting) you the switch on, the timer will be canceled.

      switchmode1 1 sets the switch in follow mode (LOW=off, HIGH=on)
      If you have inverted switch (LOW=on, HIGH=off) then use switchmode1 2


      Auto-off Motion Sense Switch~

      Example works fine on a Wemos D1 Mini. Used as night light with motion sensor or as ambient light on floor or kitchen.

      Connect an LED Strip WS2812 on D1 and the PIR on D2 and a LDR on A0 (voltage divider with 10k ohm resistor)

      SwitchMode1 1

      Rule~

      Rule1
      +  ON analog#a0<400 DO Backlog Rule3 0; Rule2 1 ENDON
      +  ON analog#a0>500 DO Backlog Rule2 0; Rule3 1 ENDON
      +
      Rule2
      +  ON switch1#state DO Backlog Power1 1; RuleTimer1 30 ENDON
      +  ON Rules#Timer=1 DO Power1 off ENDON
      +

      Rule3
      +  ON switch1#state DO Power1 off ENDON
      +

      Activate Rule1 with one shot detection
      Backlog Rule1 1; Rule1 6

      Optional
      Backlog Rule2 4; Rule3 4

      Result~

      on analog#a0>400
      disable Rule3 and activate Rule2

      on analog#a0>500
      disable Rule2 and activate Rule3

      Rule2 activates the LEDs for RuleTimer1 30 seconds on each trigger from PIR the RuleTimer start again.

      on Rules#Timer=1 do Power1 off
      The LEDs turn off after the RuleTimer expires

      Rule3 is active on daylight and pipe the PIR signal in a Power1 off signal. The LEDs stay off.


      Auto-off if or when current is idle~

      The example is used on a Sonoff POWR316D.

      It assumes an idle current of less than 0.1 amps and a grace period of 10 minutes to get consumption above the idle level.

      If the consumer uses less than 0.1 amps for more than 10 minutes, then it will be turned off.

      This works with either manually turning on power, using a timer or whatever.

      Rule~

      Rule1
      +  ON system#boot DO var1 0 ENDON
      +  ON ENERGY#Current==0.0 DO if (var1!=0) RuleTimer1 0; var1 0 endif BREAK
      +  ON ENERGY#Current>=0.1 DO if (var1!=0) RuleTimer1 0; var1 0 endif BREAK
      +  ON ENERGY#Current<0.1 DO if (var1!=1) RuleTimer1 600; var1 1 endif ENDON
      +  ON Rules#Timer=1 DO Power1 off ENDON
      +

      Control Timers from a Switch~

      Assuming that your switch is on GPIO00 and configured as Switch1:

      Switchmode1 1 will make Switch1#state be 1 when ON and 0 when OFF

      If you don't set Switchmode1 or it is equal 0, it will only have Switch1#state=2 (toggle) and the rule will not work.

      Rule~

      Rule1
      +  ON Switch1#state=1 DO Timers 0 ENDON
      +  ON Switch1#state=0 DO Timers 1 ENDON
      +

      Toggle Relay when holding button for 2 seconds~

      The following example is to explain how to catch and use the HOLD feature for buttons.

      Behavior: Disable Button1 Short Press and Toggle Relay1 only when holding button1 for 2 Seconds.

      Backlog ButtonTopic 0; SetOption1 1; SetOption32 20
      +

      Rule1
      +  ON button1#state=3 DO Power1 2 ENDON
      +  ON button1#state=2 DO delay ENDON
      +
      Rule1 1
      +

      Commands Explanation

      ButtonTopic 0 : (default) To not use topics for buttons
      SetOption1 1 : Allow only single, double and hold press button actions
      SetOption32 20 : Set key hold time from 0.1 to 10 seconds (20 = 2 seconds)
      Rule ON button1#state=3 DO Power1 2 ENDON : When holding the button1 for 2 seconds it will toggle relay 1 (state = 3 means HOLD)
      ON button1#state=2 DO delay ENDON : Do nothing when short pressing the button1 (state = 2 means TOGGLE)
      Rule1 1 : To enable rules

      NOTE: There is no state value for "double press" for Buttons. It is designed that double press will toggle the relay. See Multi-Press Functions for more information.

      In the case you do not want the double press feature you can configure your button as switch and also set SwitchMode that fits your use case (such as SwitchMode 5 to make the switch behave like a pushbutton) [SWITCH does not support double press]

      Another example but using switch instead of button:

      Backlog SwitchTopic1 0; SwitchMode1 5; SetOption32 20
      +
      +Rule1
      +  ON switch1#state=3 DO Power1 2 ENDON
      +  ON switch1#state=2 DO delay ENDON
      +
      +Rule1 1
      +

      Make sure Light is on at night~

      Using Timers, you can set a light to turn on and off to illuminate a street/patio by night. But if the device has no power at the trigger time, then, when it powers up, the light will be off all night. So, as a fail-safe, implement a conditional control to be checked at Tasmota Startup.

      Set Timers to turn on your light at Sunset and Turn off at sunrise. Use poweronstate 0 in order to start with lights off when powering up your device. Set the following rules:

      Rule1
      +  ON Time#Initialized DO Backlog event checksunrise=%time%; event checksunset=%time% ENDON
      +  ON event#checksunset>%sunset% DO Power1 1 ENDON
      +  ON event#checksunrise<%sunrise% DO Power1 1 ENDON
      +

      The previous rules are conditionals that represent the following logic:

      IF %time%>%sunset DO Power1 1 / IF %time%<%sunrise DO Power1 1


      Turn On Light Before Dawn and At Dusk~

      Turn on light at dusk until your nighttime and again in the morning before dawn.
      (memory variable method)

      What if the sun sets after your nighttime, as in during the summer? Then the timer will turn off the light at "night", but then the Sunset timer will turn it on again, so it stays on all night.

      Rule~

      Rule1
      +  ON Time#Initialized DO event chkSun ENDON
      +  ON Time#Minute=%sunset% DO event chkSun ENDON
      +  ON Time#Minute=%mem2% DO event chkSun ENDON
      +  ON Time#Minute=%sunrise% DO event chkSun ENDON
      +  ON Time#Minute=%mem1% DO event chkSun ENDON
      +
      Rule2
      +  ON event#chkSun DO Backlog var1 0; event chkSunrise=%time%; event chkSunset=%time%; event chkmorn=%time%; event chknight=%time%; event setPower ENDON
      +  ON event#chkSunrise<%sunrise% DO var1 1 ENDON
      +  ON event#chkSunset>=%sunset% DO var1 1 ENDON
      +  ON event#chkmorn<%mem1% DO var1 0 ENDON
      +  ON event#chknight>=%mem2% DO var1 0 ENDON
      +  ON event#setPower DO Power1 %var1% ENDON
      +
      Backlog mem1 360; mem2 1350; Rule1 1; Rule2 1
      +

      Result~

      • When device restarts, calculate if the light should be on or off
        ON Time#Initialized DO event chkSun ENDON

      • Calculate if the light should be on or off
        ON Time#Minute=%sunset% DO event chkSun ENDON ON Time#Minute=%mem2% DO event chkSun ENDON ON Time#Minute=%sunrise% DO event chkSun ENDON ON Time#Minute=%mem1% DO event chkSun ENDON

      • Calculate if the light should be on or off
        on event#chkSun do Backlog

      • Assume off
        var1 0;

      • Trigger each event with the current time
        event chkSunrise=%time%; event chkSunset=%time%; event chkmorn=%time%; event chknight=%time%; event setPower

      • End rule
        ENDON

      • If before sunrise, turn on
        ON event#chkSunrise<%sunrise% DO var1 1 ENDON

      • If past sunset, turn on
        ON event#chkSunset>=%sunset% DO var1 1 ENDON

      • But if before Morning time (mem1), do not turn on
        ON event#chkmorn<%mem1% DO var1 0 ENDON

      • Or if after Night time (mem2), do not turn on
        ON event#chknight>=%mem2% DO var1 0 ENDON

      • Perform on/off state
        ON event#setPower DO Power1 %var1% ENDON

      • Set variables for Morning (06h00) and Night (22h30) times
        Backlog mem1 360; mem2 1350

      • Turn on the rule sets
        Backlog Rule1 1; Rule2 1


      Turn On Light Before Dawn and At Dusk~

      Turn on light at dusk until your nighttime and again in the morning before dawn.
      (Web UI timer method)

      What if the sun sets after your nighttime, as in during the summer? Then the timer will turn off the light at "night", but then the Sunset timer will turn it on again, so it stays on all night. This version uses the timers to set the actual time, using the %timerN% variables made availible in Tasmota V11. As a result, while the rule still needs to be applied by a skilled user, a less savvy family member can next choose or modify the desired times.

      Rule~

      Rule1
      +ON Time#Initialized DO event checktime=%time% ENDON
      +ON Clock#Timer DO event checktime=%time% ENDON
      +ON event#checktime DO %var10% 0 ENDON
      +ON event#checktime>=%timer1% DO var10 1 ENDON
      +ON event#checktime>=%timer2% DO var10 0 ENDON
      +ON event#checktime>=%timer3% DO var10 1 ENDON
      +ON event#checktime>=%timer4% DO var10 0 ENDON
      +ON event#checktime DO Power1 %var10% ENDON
      +

      You do need to make sure the timers are set to run rules instead of hard ON-OFF. Timer 1,3 are interpreted as ON, Timer 2,4 as OFF. Here are some example timers, on at 06h00, off at 23h00, but you can also set these in the Web UI

      Timer1 {"Enable":1,"Mode":0,"Time":"06:00","Window":0,"Days":"1111111","Repeat":1,"Output":2,"Action":3}
      +Timer2 {"Enable":1,"Mode":1,"Time":"00:00","Window":0,"Days":"1111111","Repeat":1,"Output":2,"Action":3}
      +Timer3 {"Enable":1,"Mode":2,"Time":"00:00","Window":0,"Days":"1111111","Repeat":1,"Output":2,"Action":3}
      +Timer4 {"Enable":1,"Mode":0,"Time":"23:00","Window":0,"Days":"1111111","Repeat":1,"Output":2,"Action":3}
      +

      The basic rule above works for all situations where the sun (with or without offset) or scheduled time does not pass midnight. The more advanced version below works (from #16914 onward) also if the sunset or scheduled time is after midnight, or even if there is no sunset at all (permanent daylight or night in north scandinavia)

      Rule1
      +ON Time#Initialized DO event checktime=%time% ENDON
      +ON Clock#Timer DO event checktime=%time% ENDON
      +ON event#checktime DO Backlog var1 %timer1%; var2 %timer2%; var3 %timer3%; var4 %timer4%; var5 %value%; var6 %value%; var10 0; event checknoon=%value%; ENDON
      +ON var1#state>1140 DO sub1 1440 ENDON
      +ON var2#state>1140 DO sub2 1440 ENDON
      +ON var5#state>1140 DO sub5 1440 ENDON
      +ON var3#state<=420 DO add3 1440 ENDON
      +ON var4#state<=420 DO add4 1440 ENDON
      +ON var6#state<=420 DO add6 1440 ENDON
      +ON event#checknoon DO Backlog event checkafternoon=%var6% ENDON
      +ON event#checknoon<=780 DO Backlog event checkmorning=%var5%; event settime ENDON
      +ON event#checknoon>780 DO Backlog event checkafternoon=%var6%; event settime ENDON
      +ON event#checkmorning>=%var1% DO var10 1 ENDON
      +ON event#checkmorning>=%var2% DO var10 0 ENDON
      +ON event#checkafternoon>=%var3% DO var10 1 ENDON
      +ON event#checkafternoon>=%var4% DO var10 0 ENDON
      +ON event#settime DO Power1 %var10% ENDON
      +
      For an attempted explanation of above advanced rule, please refer to the design and test XLS in #16914


      Enable a PIR Switch only at night~

      Latitude and Longitude need to be set in config. Use PulseTime to specify the duration the light should remains on. Every PIR trigger will restart for that amount of time.

      SwitchMode1 14
      +PulseTime 60
      +
      Rule1
      +  ON Switch1#state=1 DO Backlog event checksunrise=%time%; event checksunset=%time% ENDON
      +  ON event#checksunrise<%sunrise% DO Power1 1 ENDON
      +  ON event#checksunset>%sunset% DO Power1 1 ENDON
      +

      Control luminance switch with Timer~

      Background: Tasmota powers a Sonoff Basic attached to a TS-2561 Luminance Sensor. This switch toggles a lamp ON or OFF. The switch should work as below: i) during daytime (sunrise-sunset): ON when it is too dark (<150 lx) and OFF when it gets brighter (>175 lx). ii) during evenings it ignores the sensor and turns on at sunset and turns off after about 5 hours

      Approach: Used a combination of Clock Timers and Rule to do this.

      Timer 1: Power ON switch at Sunset
      Powers on the switch at sunset with an offset of 20 minutes. Repeats every day.

      Timer1 {"Enable":1,"Mode":2,"Time":"-00:20","Window":0,"Days":"1111111","Repeat":1,"Output":1,"Action":1}
      +

      Timer 2: Power OFF switch at Night.
      Turns power OFF at 23.00hrs. Repeats every day.

      Timer2 {"Enable":1,"Mode":0,"Time":"23:00","Window":0,"Days":"1111111","Repeat":1,"Output":1,"Action":0}
      +

      Timer 3: Trigger Luminance Rule at Sunrise
      Start watching the Lux sensor 15 minutes after sunrise.

      Timer3 {"Enable":1,"Mode":1,"Time":"00:15","Window":0,"Days":"1111111","Repeat":1,"Output":1,"Action":3}
      +

      Rule 1: Main Rule to check Luminance
      If Luminance is less than 150lx, power ON. If it goes beyond 175lx, power OFF.

      Rule1
      +  ON tele-TSL2561#Illuminance<150 DO Power1 1 ENDON
      +  ON tele-TSL2561#Illuminance>175 DO Power1 0 ENDON 
      +
      +Rule1 1
      +```haskell
      +
      +**Rule 2:** Trigger Rule1 only in the Mornings  
      +This ensures that Rule1 is triggered when Timer3 starts (in the morning) and stops when Timer1 starts (in the evenings).  
      +```haskell
      +Rule2
      +  ON Clock#Timer=3 DO Rule1 1 ENDON
      +  ON Clock#Timer=1 DO Rule1 0 ENDON
      +
      +Rule2 1
      +

      Automatically vary the color temperature of a CCT light~

      Background: Tasmota powers a CCT light which is adjustable from cool white to warm white. You want the color temperature to automatically vary between cool (CT=153) at midday to warm (CT=500) at midnight.

      Approach: Check that your timezone and DST (if applicable) are set correctly and the time is correctly set on your light using NTP. Create the two rules to scale the CT based on the time of day. If Wifi is unavailable or NTP can't determine the time of day then the light will default to the mid point for neutral white (CT=326). Adjusting the CT will switch the light on so Rule 2 disables and enables Rule 1 according to the state of the light.

      Rule 1: Set the CT according to the time of day.

      rule1 
      +    on Power1#Boot do CT 326 endon
      +    on Time#Initialized do backlog event myCT=%time% endon
      +    on Time#Minute do backlog event myCT=%time% endon
      +    on event#myCT<=720 do backlog scale1 %time%,0,720,500,153; event updateCT endon
      +    on event#myCT>720 do backlog scale1 %time%,720,1440,153,500; event updateCT endon
      +    on event#updateCT do CT %var1% endon
      +rule1 1
      +
      Rule 2: Toggle rule 1 on and off with the light and also update the CT for the current time when the light is switched on.

      Rule2 on Power1#State do backlog Rule1 %value%; event myCT=%time% endon
      +Rule2 1
      +
      Notes:

      When the light is powered up on it should change to neutral white for a few seconds while Wifi, MQTT and NTP are initialised then switch to the correct color temperature for the time of day. If the light is switched off in software then it will come back on with the previous CT then update to the current CT after a few seconds.

      If you have a RGBCCT light then the CT light may be Power2 rather than Power1.

      It is possible to use %sunrise% and %sunset% to adjust the CT if you set your latitude, longitude and elevation but it's much more complex especially at extreme Northern and Southern latitudes.


      Perform any action on single/double press (for switches AND buttons)~

      Rule~

      SwitchMode 5

      Rule1
      +  ON switch1#state==2 DO add1 1 ENDON
      +  ON switch1#state==2 DO Power1 2 ENDON
      +  ON var1#state!=0 DO Backlog delay 6;var1 0 ENDON
      +  ON var1#state==2 DO publish cmnd/othertasmota/POWER toggle ENDON
      +

      Rule1 on

      Result~

      • each toggle of the switch triggers first condition and adds 1 to our variable (var1 in the example),
      • each toggle of the switch toggles the associated relay (Power1 2 - but can do anything else instead, Publish for example)
      • when var1 changes to non zero, we set it back to 0 but after a Delay (arbitrarily chosen 6 here - 0.6 seconds)
      • when var1 reaches 2 (i.e. the switch has been toggled twice within the last 0.6 seconds), desired action is triggered (here: Publish to othertasmota)

      Every time you press the switch, your light toggles state (as it should). If you do press the switch twice in a rapid succession (i.e., double-click), you can trigger a different action (e.g., on a remote device).


      Enable or disable relay with a switch in Domoticz~

      When you want to send MQTT messages ( we use domoticz in this example ) and choose when you want the relay on or off, by simply sending HTTP commands to trigger an event.

      Initial Config:

      • PushButton Doorbell
      • (Sonoff Basic R1) GPIO14 - Switch4 (12)

      Connect the Switch to GND and the GPIO on your device. Be sure put a 4.7k resistor between VCC(3.3v) and the GPIO. This prevents ghost switching (capacitor is optional) See: YouTube

      Dont forget to change the IDX value

      Commands:

      Backlog SwitchTopic 0; SwitchMode4 2; SetOption0 0; PowerOnState 0
      +
      +var1 1
      +
      +Rule1
      +  ON event#doorbell DO var1 %value% ENDON
      +  ON switch4#state=1 DO publish domoticz/in {"idx":11,"nvalue":1} ENDON
      +  ON switch4#state=1 DO Power1 %var1% ENDON
      +  ON switch4#state=0 DO publish domoticz/in {"idx":11,"nvalue":0} ENDON
      +  ON switch4#state=0 DO Power1 0 ENDON
      +
      +Rule1 1
      +

      Usage:

      Turn off the relay by calling the event using HTTP:
      http://<tasmotaIP>/cm?cmnd=event%20doorbell=0

      Turn on the relay by calling the event using HTTP:
      http://<tasmotaIP>/cm?cmnd=event%20doorbell=1

      If your Tasmota device is password protected, which is most common, then use the following HTTP commands instead. Make sure you change <tasmotaUsername> and <tasmotaPassword>

      Off:
      http://<tasmotaIP>/cm?&user=<tasmotaUsername>&password=<tasmotaPassword>&cmnd=event%20doorbell=0
      On:
      http://<tasmotaIP>/cm?&user=<tasmotaUsername>&password=<tasmotaPassword>&cmnd=event%20doorbell=1


      Force automatic re-connection to MQTT server via SD DNS~

      In order to search for the MQTT server using SD-DNS service (a.k.a. Bonjour or Zero Network Configuration) the suggested configuration is to leave the MQTT Host field blank.

      The standard behavior of Tasmota is

      • searches for _mqtt._tcp service
      • resolve that to the proper IP address
      • connect to it
      • in case the connection is successful, retain the IP address and use that in the subsequent connections

      The above is not proper, though, in case you have a redundant MQTT (e.g., two MQTT server synchronized). In such case, when the active MQTT fails for any reason, the expected behavior is to achieve automatic re-connection to the other MQTT server.

      That can be easily configured defining the following rule on the device console:

      Rule1 ON Mqtt#Disconnected DO MqttHost 0 ENDON
      +Rule1 1
      +

      If the MqttHost field already contains an IP, you have to delete it using the web interface or the following MQTT command:

      mosquitto_pub -h mqtt_server.local -t "cmnd/mqttTopic/MqttHost" -m ''
      +

      Change distance to percentage~

      When measuring distance and you have the need to see it in percentage of distance. In the example 100% is everything below 69cm and 0% is everything above 128cm. This is used for showing fill percentage of a wood pellets storage.

      Rule1
      +  ON tele-SR04#distance DO Backlog var1 %value%; event checklimit=%value%; event senddistance ENDON
      +  ON event#checklimit>128 DO var1 128 ENDON
      +  ON event#checklimit<69 DO var1 68 ENDON
      +  ON event#senddistance DO Backlog SCALE1 %var1%, 128, 69, 0, 100; event pubdata ENDON
      +  ON event#pubdata DO publish tele/pannrum-temp/SENSOR %var1% ENDON
      +
      +Rule1 1
      +

      Distinguish Switch1 and Switch2 (without the use of Relay1 and Relay2)~

      When two (or more) switches are defined as input and you want to distinguish these in the RESULT topic without the use of Relays, then consider the following rules.

      • SwitchMode1 1 will make Switch1#state to be 1 when ON and 0 when OFF
        SwitchMode1 1

      • SwitchMode2 1 will make Switch2#state to be 1 when ON and 0 when OFF
        SwitchMode2 1

      • Publish json with key POWER1 and value %value%

        Rule1 ON switch1#state DO publish stat/wemos-4/RESULT {"POWER1":"%value%"} ENDON
        +

      • Publish json with key POWER2 and value %value%

        Rule2 ON switch2#state DO publish stat/wemos-4/RESULT {"POWER2":"%value%"} ENDON
        +

      • Enable Rules Backlog Rule1 1; Rule2 1

      Output:

      RUL: SWITCH1#STATE performs "publish stat/wemos-4/RESULT {"POWER1":"1"}"
      +MQT: stat/wemos-4/RESULT = {"POWER1":"1"}
      +RUL: SWITCH2#STATE performs "publish stat/wemos-4/RESULT {"POWER2":"1"}"
      +MQT: stat/wemos-4/RESULT = {"POWER2":"1"}
      +RUL: SWITCH1#STATE performs "publish stat/wemos-4/RESULT {"POWER1":"0"}"
      +MQT: stat/wemos-4/RESULT = {"POWER1":"0"}
      +RUL: SWITCH2#STATE performs "publish stat/wemos-4/RESULT {"POWER2":"0"}"
      +MQT: stat/wemos-4/RESULT = {"POWER2":"0"}
      +RUL: SWITCH1#STATE performs "publish stat/wemos-4/RESULT {"POWER1":"1"}"
      +MQT: stat/wemos-4/RESULT = {"POWER1":"1"}
      +RUL: SWITCH1#STATE performs "publish stat/wemos-4/RESULT {"POWER1":"0"}"
      +MQT: stat/wemos-4/RESULT = {"POWER1":"0"}
      +

      Receiving state of anything that triggers SWITCH more than one time~

      With analog intercom doorbells you can take out info about ringing from speaker voltage. You can connect GPIO to it via opto-isolator and resistor to take out state. But even with those speaker voltage is dropping so it switches the device multiple times.

      MQT: cmnd/doorbell/POWER2 = OFF (retained)
      +MQT: cmnd/doorbell/POWER2 = ON (retained)
      +MQT: cmnd/doorbell/POWER2 = OFF (retained)
      +MQT: cmnd/doorbell/POWER2 = ON (retained)
      +MQT: cmnd/doorbell/POWER2 = OFF (retained)
      +

      To solve it we can use rules.

      SwitchTopic 0
      +
      +Rule1
      +  on System#Boot DO var1 0 ENDON
      +  ON Switch2#State DO Backlog add1 1; event START ENDON
      +  ON event#START DO event BELL=%var1% ENDON
      +  ON event#BELL=1.000 DO Backlog publish cmnd/bell/power on; RuleTimer1 60 ENDON
      +  ON event#BELL=0 DO publish cmnd/bell/power off ENDON
      +  ON Rules#Timer=1 DO Backlog var1 0; event BELL=0 ENDON
      +
      +Rule1 1
      +

      description:

      • Disable SwitchTopic as it overrides rules for switches: SwitchTopic 0
      • on system boot set var1 to 0
      • on switch2 click (person pushing doorbell) - var1 += 1; trigger event START
      • on START - set event BELL equal to var1
      • if event#BELL=1 (triggered first time) publish mqtt message ON and trigger RulesTimer1 for 60 seconds
      • if event#BELL=0 publish mqtt message OFF
      • on RulesTimer1 - reset var1 to 0, and call event#BELL.
      • enable rule 1

      In this case we have lock for 60 seconds for multiple people calls or to be resistant for speaker voltage drops.


      Prevent Wemos D1 mini load overcurrent~

      As a WS2812 24 led ring draws approximately 24x3x20 mA = 1.44A and the Wemos D1 mini powered from a PC's USB port can only provide up to 0.5A it would be nice to have some kind of mechanism in place to limit the amount of current to the WS2812 LEDring to 0.1A. This is still enough to light all 24 leds up to color 202020.

      Hardware

      • Wemos D1 mini
      • INA219 I2C sensor
      • WS2812 LEDring with 24 LEDs powered by the Wemos D1 mini 5V thru the INA219 sensor
      Rule1 ON INA219#Current>0.100 DO Backlog Dimmer 10;Color 10,0,0 ENDON
      +Rule1 on
      +

      Result - When a user raises brightness to a level using more than 0.1A the rule kicks in and lowers the current by executing command Dimmer 10 and changes the color to Red with command Color 10,0,0.


      Using dummy GPIO to send Serial codes to an MCU~

      By having a device that controls all its features through an MCU and reports the states in serial codes to the ESP8266 we have to create some rules to control it using the Web UI or standard Power commands.

      Rule1
      +  ON Power1#state=1 DO serialsend5 55AA00060005020400010213 ENDON 
      +  ON Power1#state=0 DO serialsend5 55AA00060005020400010011 ENDON 
      +  ON Power2#state=1 DO serialsend5 55AA00060005060400010217 ENDON 
      +  ON Power2#state=0 DO serialsend5 55AA00060005060400010015 ENDON
      +
      Power1 controls the device, Power2 turn on and off the light on the device.

      Another rule was created to issue commands on boot so the serial interface works every time and to control the built in fan using Event triggers and have its state retained in an MQTT message for Home Assistant.

      Rule2 
      +  ON system#boot DO Backlog baudrate 9600; seriallog 2; serialsend5 55aa000300010306 ENDON 
      +  ON event#high DO Backlog serialsend5 55AA00060005650400010175; publish2 stat/diffuser/FAN high ENDON 
      +  ON event#low DO Backlog serialsend5 55AA00060005650400010074; publish2 stat/diffuser/FAN low ENDON
      +

      Arithmetic commands used with VAR~

      Note

      The arithmetic is done using single point precision floating point. This means calculations involving values larger than approximately 16 million (ex: %utctime%) will not be precise.

      ADD~

      ADD1 to ADD5: Add a value to VARx
      Syntax: ADDx value
      Usage: ADD1 15
      Result: VAR1 = VAR1 + 15

      SUBTRACT~

      SUB1to SUB5: Subtract a value from VARx
      Syntax: SUBx value
      Usage: SUB1 15
      Result: VAR1 = VAR1 - 15

      MULTIPLY~

      MULT1to MULT5: Multiply a value to VARx
      Syntax: MULTx value
      Usage: MULT1 15
      Result: VAR1 = VAR1 * 15

      SCALE A VALUE~

      SCALE1to SCALE5: Scale a value from a low and high limit to another low and high limit and store it in VARx (directly equivalent to MAP arduino command)

      Syntax: SCALEx value, fromLow, fromHigh, toLow, toHigh

      where,

      value: the number to scale
      fromLow: the lower bound of the value’s current range
      fromHigh: the upper bound of the value’s current range
      toLow: the lower bound of the value’s target range
      toHigh: the upper bound of the value’s target range

      (omitted values are taken as zero)

      Usage: SCALE1 15, 0, 100, 0, 1000
      Result: VAR1 = 150


      Transmit sensor value only when a delta is reached~

      Send only when the sensor value changes by a certain amount.

      Rule1
      +  ON SI7021#temperature>%var1% DO Backlog var1 %value%; publish stat/mqttTopic/temp %value%; var2 %value%; add1 2; sub2 2 ENDON
      +  ON SI7021#temperature<%var2% DO Backlog var2 %value%; publish stat/mqttTopic/temp %value%; var1 %value%; add1 2; sub2 2 ENDON
      +

      This example explains expands on the above example while matching typical sensor data. Helpful for HA tasmota integration sensors when polling and adding in delta value changes.

      Normal polling data below

      23:58:41 MQT: tele/ds1820/SENSOR = {"Time":"2021-01-13T23:58:41","DS18B20":{"Id":"030597946B04","Temperature":20.9},"TempUnit":"C"}
      +
      The matching rule.

      Rule1 
      +  ON DS18B20#temperature>%var1% DO Backlog var1 %value%; publish tele/ds1820/SENSOR {"Time":"%timestamp%","DS18B20":{"Id":"030597946B04","Temperature":%value%},"TempUnit":"C"}; var2 %value%; add1 0.5; sub2 0.5 ENDON 
      +  ON DS18B20#temperature<%var2% DO Backlog var2 %value%; publish tele/ds1820/SENSOR {"Time":"%TIMESTAMP%","DS18B20":{"Id":"030597946B04","Temperature":%value%},"TempUnit":"C"}; var1 %value%; add1 0.5; sub2 0.5 ENDON
      +

      Adjust a value and send it over MQTT~

      This example adds 2 degrees to the measured temperature and then sends that value to an MQTT topic.

      Rule1
      +  ON tele-SI7021#temperature DO Backlog var1 %value%; add1 2; event sendtemp ENDON
      +  ON event#sendtemp DO publish stat/mqttTopic/temp %var1% ENDON
      +

      Control relays via serial~

      This example switches connected relays over the software serial on and off.

      Write the following rules:

      rule1
      +  ON SSerialReceived#Data=on DO Power1 1 ENDON
      +  ON SSerialReceived#Data=off DO Power1 0 ENDON
      +

      receiving on and off results in

      MQT: tele/mqttTopic/RESULT = {"SSerialReceived":"on"}
      +RUL: SSERIALRECEIVED#DATA=ON performs "Power1 1"
      +MQT: stat/mqttTopic/RESULT = {"POWER":"ON"}
      +MQT: stat/mqttTopic/POWER = ON
      +MQT: tele/mqttTopic/RESULT = {"SSerialReceived":"off"}
      +RUL: SSERIALRECEIVED#DATA=OFF performs "Power1 0"
      +MQT: stat/mqttTopic/RESULT = {"POWER":"OFF"}
      +MQT: stat/mqttTopic/POWER = OFF
      +

      Processing JSON received from (Software)SerialBridge~

      When using SerialBridge (or SoftwareSerialBridge), the received string will be published to Rules as SerialReceived (or SSerialReceived). If the string starts with a { then Tasmota will parse the string as a JSON and make the different keys available for Rules. For example with the following string {"DeviceID":"TM182","Temp":25.3,"Hum":50}, it is possible to use any of the keys in the trigger.

      rule1
      +  ON SSerialReceived#DeviceID DO var1 %value% ENDON
      +  ON SSerialReceived#Temp DO var2 %value% ENDON
      +  ON SSerialReceived#Hum DO publish /some/topic/%var1% {"Temperature":%var2%,"Humidity":%value%} ENDON
      +

      The 1st and 2nd rules store the values for Device and Temp into variables. The last key triggers the 3rd rule, here re-publication on a different topic.

      Execution:

      12:51:48.050 MQT: tele/nodemcu/SSERIALRECEIVED = {"SSerialReceived":{"DeviceID":"TM182","Temp":25.3,"Hum":50}}
      +12:51:48.064 RUL: SSERIALRECEIVED#DEVICEID performs "var1 TM182"
      +12:51:48.071 MQT: stat/nodemcu/VAR = {"Var1":"TM182"}
      +12:51:48.083 RUL: SSERIALRECEIVED#TEMP performs "var2 25.3"
      +12:51:48.091 MQT: stat/nodemcu/VAR = {"Var2":"25.3"}
      +12:51:48.104 RUL: SSERIALRECEIVED#HUM performs "publish /some/topic/TM182 {"Temperature":25.3,"Humidity":50}"
      +12:51:48.110 MQT: /some/topic/TM182 = {"Temperature":25.3,"Humidity":50}
      +

      Note

      It is important that the receive string strictly starts with the opening {. If other characters, such as spaces or new line are inserted before, Tasmota will not par as a JSON. Characters after the closing } are not a problem.


      Using BREAK to simulate IF..ELSEIF..ELSE..ENDIF~

      BREAK is an alternative to ENDON. BREAK will stop the execution for the triggers that follow. If a trigger that ends with BREAK fires, then the following triggers of that rule will not be executed. This allows to simulate IF..ELSEIF..ELSE..ENDIF

      Example:

      IF temp > 85 then
      +  VAR1 more85
      +ELSEIF temp > 83 then
      +  VAR1 more83
      +ELSEIF temp > 81 then
      +  VAR1 more81
      +ELSEIF temp = 81 then
      +  VAR1 equal81
      +ELSE
      +  VAR1 less81
      +ENDIF
      +

      With the actual rules, if we use a set like the following:

      Rule1
      +  ON event#temp>85 DO VAR1 more85 ENDON
      +  ON event#temp>83 DO VAR1 more83 ENDON
      +  ON event#temp>81 DO VAR1 more81 ENDON
      +  ON event#temp=81 DO VAR1 equal81 ENDON
      +  ON event#temp<81 DO VAR1 less81 ENDON
      +

      This is the output in the console:

      CMD: rule
      +MQT: stat/living/RESULT = {"Rule1":"ON","Once":"ON","StopOnError":"OFF","Free":322,"Rules":"ON event#temp>85 do VAR1 more85 ENDON ON event#temp>83 do VAR1 more83 ENDON on event#temp>81 do VAR1 more81 ENDON on event#temp=81 do VAR1 equal81 ENDON on event#temp<81 DO VAR1 less81 ENDON"}
      +CMD: event temp=10
      +MQT: stat/living/RESULT = {"Event":"Done"}
      +RUL: EVENT#TEMP<81 performs "VAR1 less81"
      +MQT: stat/living/RESULT = {"Var1":"less81"}
      +CMD: event temp=100
      +MQT: stat/living/RESULT = {"Event":"Done"}
      +RUL: EVENT#TEMP>85 performs "VAR1 more85"
      +MQT: stat/living/RESULT = {"Var1":"more85"}
      +RUL: EVENT#TEMP>83 performs "VAR1 more83"
      +MQT: stat/living/RESULT = {"Var1":"more83"}
      +RUL: EVENT#TEMP>81 performs "VAR1 more81"
      +MQT: stat/living/RESULT = {"Var1":"more81"}
      +
      So, all the triggers where TEMP>100, are firing. With the BREAK statement the rule set can be changed to:
      Rule
      +  on event#temp>85 do VAR1 more85 break
      +  on event#temp>83 do VAR1 more83 break
      +  ON event#temp>81 DO VAR1 more81 ENDON
      +  ON event#temp=81 DO VAR1 equal81 ENDON
      +  ON event#temp<81 DO VAR1 less81 ENDON
      +

      Which will result in the following output:

      CMD: rule
      +RSL: RESULT = {"Rule1":"ON","Once":"OFF","StopOnError":"OFF","Free":321,"Rules":"ON event#temp>85 do VAR1 more85 break ON event#temp>83 do VAR1 more83 break on event#temp>81 do VAR1 more81 ENDON on event#temp=81 do VAR1 equal81 ENDON on event#temp<81 DO VAR1 less81 ENDON"}
      +CMD: event temp=10
      +RSL: RESULT = {"Event":"Done"}
      +RUL: EVENT#TEMP<81 performs "VAR1 less81"
      +RSL: RESULT = {"Var1":"less81"}
      +CMD: event temp=100
      +RSL: RESULT = {"Event":"Done"}
      +RUL: EVENT#TEMP>85 performs "VAR1 more85"
      +RSL: RESULT = {"Var1":"more85"}
      +CMD: event temp=83
      +RSL: RESULT = {"Event":"Done"}
      +RUL: EVENT#TEMP>81 performs "VAR1 more81"
      +RSL: RESULT = {"Var1":"more81"}
      +


      Adjust PowerDelta according to current Power values~

      Power sensor reporting thresholds are set by a percentage change in the Power value by setting PowerDelta. Power changes from 10W to 11W (10%) may not be very interesting. But power changes from 1000W to 1100W (also 10%) could be very important. To avoid getting reports for small changes but ensuring that larger power swings are reported, a rule set can be used to create a gradient threshold based on the absolute power values.

      This rule also uses the one-shot feature of rules to avoid reporting of every small change within a threshold window. The rule (a ON/DO/ENDON rule in this the set) will trigger only once when a threshold is crossed.

      Backlog PowerDelta 0; Rule1 0; Rule1 5
      +
      +Rule1
      +  ON ENERGY#Power>=35 DO Backlog PowerDelta 10; Status 8 BREAK 
      +  ON ENERGY#Power>=15 DO Backlog PowerDelta 25; Status 8 BREAK 
      +  ON ENERGY#Power>5 DO Backlog PowerDelta 35; Status 8 BREAK 
      +  ON ENERGY#Power<=5 DO PowerDelta 100 ENDON
      +
      +Rule1 1
      +

      Which translates (pseudo code):

      IF ENERGY#Power>=35  // ENERGY#Power GE 35
      +  DO Backlog PowerDelta 10; Status 8
      +ELSE IF ENERGY#Power>=15  // ENERGY#Power GE 15 and LT 35
      +  DO Backlog PowerDelta 25; Status 8
      +ELSE IF ENERGY#Power>5  // ENERGY#Power GT 5 and LT 15
      +  DO Backlog PowerDelta 35; Status 8
      +ELSE  // ENERGY#Power changed (i.e. LE 5)
      +  DO PowerDelta 100
      +


      Forward IR signals~

      Using one IR receiver and one sender (or both extender) you can simply forward signals from one to another using the following rule

      rule1 ON IRreceived#Data DO publish cmnd/irsideboard/irsend {Protocol:NEC,Bits:32,Data:%value%} ENDON
      +


      Garage Door Opener~

      (#3942)

      // Set the relay on time to signal the opener
      PulseTime 7

      // Send ON and OFF as the switch is ON or OFF

      Backlog SwitchMode1 1; SwitchMode2 1; SwitchMode3 1
      +

      //No need to save changes on power cycle
      SetOption0 0

      //Don’t blindly run the door on power up
      PowerOnState 0

      //One shot Detection off

      Backlog Rule1 0; Rule1 4; Rule2 0; Rule2 4; Rule2 0; Rule2 4
      +

      //Set Counter to measure the period between on and off, check if it's blinking because of an obstruction

      Backlog CounterType 1; CounterDebounce 100
      +

      //So the door doesn't close if you send it an Open when it's already Opened, etc.

      // var1=1 Only When OPEN  
      +// var2=1 Only When CLOSED  
      +// var3=1 Only When OPENING  
      +// var4=1 Only When CLOSING  
      +
      Rule1
      +  ON Switch1#Boot=1 DO Backlog delay 99; event Opened ENDON
      +  ON Switch2#Boot=1 DO Backlog delay 99; event Closed ENDON
      +  ON EVENT#OPEN DO Power1 %var2% ENDON
      +  ON EVENT#CLOSE DO Power1 %var1% ENDON
      +  ON EVENT#STOP DO Backlog Power1 %var3%; Power1 %var4%; event PState=STOP ENDON
      +  ON Switch1#State=1 DO event Opened ENDON
      +  ON Switch2#State=1 DO event Closed ENDON
      +  ON Switch1#State=0 DO event Closing ENDON
      +  ON Switch2#State=0 DO event Opening ENDON
      +
      +Rule2
      +  ON event#Opened DO Backlog var 1; var2 0; var3 0; var4 0; ruletimer1 0; event PState=OPEN ENDON
      +  ON event#Closed DO Backlog var1 0; var2 1; var3 0; var4 0; ruletimer1 0; event PState=CLOSE ENDON
      +  ON event#Opening DO Backlog var1 0; var2 0; var3 1; var4 0; ruletimer1 15; event PState=OPENING ENDON
      +  ON event#Closing DO Backlog var1 0; var2 0; var3 0; var4 1; ruletimer1 15; event PState=CLOSING ENDON
      +
      +Rule3
      +  ON counter#c1>1000 DO event PObstr=0 ENDON
      +  ON counter#c1<1000 DO event PObstr=1 ENDON
      +  ON event#PObstr DO publish stat/GarageDoor/OBSTR %value% ENDON
      +  ON event#PState DO publish stat/GarageDoor/STATE %value% ENDON
      +  ON rules#timer=1 DO event PState=STOP ENDON
      +

      //Turn on Rules

      Backlog Rule1 1; Rule2 1; Rule3 1
      +


      IR Remote Button Multi-press~

      For example, a remote control with one button to change speed. This rules simulates pressing the button three times to set the receiving device to the third speed setting.

      Specify the rule set

      • The <trigger> can be a condition or an event sent from another device or home automation hub.
      • <topic> corresponds to the device transmitting the code (e.g., YTF IR Bridge). This could also be modified to send an RF code from a Sonoff RF Bridge.
      • The Delay may not be necessary in your environment or may need to be adjusted according to your device characteristics.
      Rule 1
      +  ON Event#tora DO Backlog Publish cmnd/<topic>/IRSend {"Protocol":"NEC","Bits":32,"Data":"0x00FF30CF"}; Delay 10 ENDON
      +  ON <trigger> DO Backlog Event tora; Event tora; Event tora ENDON
      +
      • Enable the Rule set
        Rule1 1

      Two-way light switches without MQTT~

      Two Sonoff T1 3-gang light switches can be used at either end of a room by setting up one the master and the other as the slave. The master performs the switching of the power to the lights, while the slave just asks the master to toggle the power state. The master also turns the slave's relays on and off so that the LED indicators follow the master's state.

      Using the WebSend command, the two switches can talk to each other without an MQTT broker. It remains to be seen how reliable this is.

      Starting with the slave, the rule to toggle the master is pretty simple:

      Rule1
      +  ON Event#sendPower DO WebSend [192.168.0.74] POWER%value% TOGGLE ENDON
      +  ON Button1#State DO Event sendPower=1 ENDON
      +  ON Button2#State DO Event sendPower=2 ENDON
      +  ON Button3#State DO Event sendPower=3 ENDON
      +
      Rule1 1

      Note that having a rule for the Button#State disables the power toggling of the slave's relay(s). This is desirable because we want the master to control the slave's relay state(s) according to its own as follows:

      Rule1
      +  ON Event#sendPower DO WebSend [192.168.0.144] POWER%Var1% %value% ENDON
      +  ON Power1#state DO Backlog Var1 1;Event sendPower=%value% ENDON
      +  ON Power2#state DO Backlog Var1 2;Event sendPower=%value% ENDON
      +  ON Power3#state DO Backlog Var1 3;Event sendPower=%value% ENDON
      +
      Rule1 1


      Control remote light on switch double press~

      Toggling the switch controls local POWER state while toggling twice fast controls another device.

      Great with two SONOFF MINI in adjacent rooms, to control both rooms with either switch.

      SwitchMode 8
      +Rule1 ON switch1#state=3 DO websend [ip/hostname of remote] power1 toggle ENDON
      +Rule1 1
      +

      Roller shutter push-button toggle~

      With a two relay device (e.g., Shelly 2.5) configured for a roller shutter, you can also connect push-buttons (configured as switch components in this example) and set them for inverted toggle behavior. Pressing a push-button once makes the roller shutter move in one direction. Pressing it again stops it. These rules each use a variable to remember the shutter state where 0 == Stopped and 1 == Moving.

      Backlog SwitchTopic 0; SwitchMode1 4; SwitchMode2 4
      +
      +Rule1
      +  ON Switch1#State==1 DO Add1 1 ENDON
      +  ON Var1#State==0 DO ShutterStop1 ENDON
      +  ON Var1#State==1 DO ShutterClose1 ENDON
      +  ON Var1#State>=2 DO Var1 0 ENDON
      +  ON Shutter1#Close DO Var1 0 ENDON
      +  ON Switch2#State==1 DO Add2 1 ENDON
      +  ON Var2#State==0 DO ShutterStop1 ENDON
      +  ON Var2#State==1 DO ShutterOpen1 ENDON
      +  ON Var2#State>=2 DO Var2 0 ENDON
      +  ON Shutter1#Open DO Var2 0 ENDON
      +
      +Rule1 1
      +

      Control a dimmer with one switch~

      This example is for GPIOs defined as switches not buttons

      Activate dimmer mode with Switchmode 11 and shorten long press time to 1 second (Setoption32 10).

      A short press of the switch sends a TOGGLE message to toggle the dimmer. A long press sends repeated INC_DEC messages to increment the dimmer. If a second press of the switch follows the first press an INV message is sent to invert the function from increment to decrement and repeated INC_DEC messages are sent to decrement the dimmer. After releasing the switch a timeout message CLEAR resets the automation

      Backlog SwitchMode 11; SetOption32 10; Rule1 1;
      +
      +Rule1 
      +on system#boot do var1 + ENDON
      +on switch1#state=2 do POWER TOGGLE ENDON
      +on switch1#state=4 do DIMMER %var1% ENDON
      +on switch1#state=7 do event upordown=%var1% ENDON
      +on event#upordown=+ do var1 - ENDON
      +on event#upordown=- do var1 + ENDON
      +
      Restart Tasmota after creating the rule set. Notice we use Rule which edits Rule1 rule set. They can be used interchangeably.


      Watchdog for Wi-Fi router or modem~

      The ping method requires #define USE_PING and Tasmota version 8.2.0.3 or newer

      The WebQuery method requires Tasmota version 10.0.0 or newer

      A Tasmota plug can check a remote host (router itself, something else connected to the router, or a site on the Internet) via an ICMP Ping or loading a URL and can power cycle the router or modem if the remote host isn't responding. In this example, an interval of 3 minutes is used. The simplest watchdog rule does not use variables:

      Rule1
      +  ON Time#Minute|3 DO backlog Ping4 192.168.1.10 ENDON
      +  ON Ping#192.168.1.10#Success==0 DO Backlog Power1 0; Delay 10; Power1 1; ENDON
      +Rule1 1
      +

      However, if the endpoint becomes unreachable for a long time, the watchdog will keep cycling it every three minutes. This could reduce the watchdog's relay lifetime to months, at most years. A safer option would be to use an exponential backoff algorithm. Var1 contains the current interval in minutes, which is tripled after each failed query, but limited to 1439 minutes (1 day).

      Rule1
      +  ON system#boot do Var1 3 ENDON
      +  ON Var1#State>1439 DO Var1 1439 ENDON
      +
      +  ON Time#Minute|%var1% DO backlog Ping4 192.168.1.10 ENDON
      +  ON Ping#192.168.1.10#Success==0 DO backlog Mult1 3; Power1 0; Delay 10; Power1 1 ENDON
      +  ON Ping#192.168.1.10#Success>0 DO Var1 3 ENDON
      +

      If your Tasmota doesn't have ping compiled in and your remote host has an HTTP server you can access, you can use WebQuery as below:

      Rule1
      +  ON system#boot do Var1 3 ENDON
      +  ON Var1#State>1439 DO Var1 1439 ENDON
      +
      +  ON Time#Minute|%var1% DO backlog WebQuery http://192.168.1.10/ GET ENDON
      +  ON WebQuery#Data$!Done DO backlog Mult1 3; Power1 0; Delay 10; Power1 1 ENDON
      +  ON WebQuery#Data=Done DO Var1 3 ENDON
      +

      Triggering off the JSON response to webquery (and other commands) may require wrapping the command in backlog, as per example above


      Simple Thermostat Example~

      As example, to be used on a Sonoff TH10 with Sensor Si7021

      This example turn on and off an output based on the temperature value and the upper set point and the lower set point. It waits until is enabled by pressing the button or by mqtt message 1 to mem1. This value is remembered. So if power cycle occurs, will resume operation. The set point values can be changed on the fly by mqtt or console commands If the Temperature sensor disconnects, the outputs will shutdown until the sensor is back again and will resume operation. When the device is power up, the thermostat also waits until the sensor value to resume operation.

      Initial Config:

      • Available physical button as Switch1
      • Relay1 will be used the controller
      • Rules must be used to control Relay so the pushbutton must only control Switch1 and not directly control the relay - For this we use SwitchMode1 3 as described below and create the necessary rules because the pushbutton control of the relay is only disabled when the rules are in place.

      Initial config on console:

      • SwitchMode1 3 <- Use the switch1 as pushbutton (It will allow us to disable the link between the button and the relay by inserting a rule to dictate what the pushbutton will do - NOTE: Until rules are created the pushbutton will still control the relay!)
      • Rule1 1 <- turn on rules
      • Rule1 4 <- turn off one-shot rule
      • TelePeriod 60 <- check temp every minute
      • SetOption26 1 <- use Power1 on mqtt messages
      • SetOption0 0 <- dont save relay status on eeprom
      • PowerOnState 0 <- start all relays off
      • Mem1 0 <- thermostat status: 0-off 1-enabled - View or set by MQTT cmnd/mqttTopic/mem1
      • Mem2 25 <- setpoint Temp upper limit - View or set by MQTT cmnd/mqttTopic/mem2
      • Mem3 23 <- setpoint Temp lower limit - View or set by MQTT cmnd/mqttTopic/mem3
      • Var1 0 <- thermostat actual status: 1-OK 0-NOT READY - View by MQTT cmnd/mqttTopic/var1

      Rules~

      On boot start a watchdog timer to check temp sensor connection.

      Rule ON system#boot DO RuleTimer1 70 ENDON
      +

      An available button is configured as switch to set thermostat ON or OFF

      Rule1
      +  ON switch1#state DO Backlog event toggling1=%mem1% ENDON
      +  ON event#toggling1=0 DO mem1 1 ENDON
      +  ON event#toggling1=1 DO mem1 0 ENDON
      +

      Check temp sensor connection. If fails, set to off and turn off thermostat. Also continue checking

      Rule ON Rules#Timer=1 DO Backlog var1 0; RuleTimer1 70; Power1 0 ENDON
      +

      Resets checking timer if temperature is connected

      Rule ON tele-SI7021#temperature DO Backlog var1 1; RuleTimer1 30; event ctrl_ready=1; event temp_demand=%value% ENDON
      +

      Thermostat control - upper limit and lower limit and enabled

      Rule1
      +  ON event#ctrl_ready>%mem1% DO var1 0 ENDON
      +  ON event#temp_demand>%mem2% DO Power1 0 ENDON
      +  ON event#temp_demand<%mem3% DO Power1 %var1% ENDON
      +

      Thermostat can be turned On by:

      • pushing button
      • by command on local console: mem1 1
      • by command on any other console: publish cmnd/mqttTopic/mem1 1
      • or MQTT at: cmnd/mqttTopic/mem1 1

      Thermostat can be turned Off by:

      • pushing button
      • by command on local console: mem1 0
      • by command on any other console: publish cmnd/mqttTopic/mem1 0
      • or MQTT at: cmnd/mqttTopic/mem1 0

      To get the status:

      • mem1 <- thermostat status: 0-off 1-enabled - View or set by MQTT cmnd/mqttTopic/mem1
      • mem2 <- setpoint Temp upper limit - View or set by MQTT cmnd/mqttTopic/mem2
      • mem3 <- setpoint Temp lower limit - View or set by MQTT cmnd/mqttTopic/mem3
      • var1 <- thermostat actual status: 1-OK 0-NOT READY - View by MQTT cmnd/mqttTopic/var1

      Everything together:

      Initial config:

      RuleTimer1 must be greater that TelePeriod for expected results

      Backlog SwitchMode1 3; Rule 1; Rule 4; TelePeriod 60; SetOption26 1; SetOption0 0; poweronstate 0; mem1 0; mem2 25; mem3 23; var1 0
      +

      Rules

      Rule1 
      +  ON system#boot DO RuleTimer1 70 ENDON 
      +  ON Switch1#State DO event toggling1=%mem1% ENDON 
      +  ON event#toggling1=0 DO mem1 1 ENDON 
      +  ON event#toggling1=1 DO mem1 0 ENDON 
      +  ON Rules#Timer=1 DO Backlog var1 0; RuleTimer1 70; Power1 0 ENDON 
      +  ON tele-SI7021#temperature DO Backlog var1 1; RuleTimer1 70; event ctrl_ready=1; event temp_demand=%value% ENDON 
      +  ON event#ctrl_ready>%mem1% DO var1 0 ENDON 
      +  ON event#temp_demand>%mem2% DO Power1 0 ENDON 
      +  ON event#temp_demand<%mem3% DO Power1 %var1% ENDON
      +

      Example rules without temp sensor to test the thermostat rules

      Rule1 
      +  ON system#boot DO RuleTimer1 70 ENDON 
      +  ON Switch1#State DO event toggling1=%mem1% ENDON 
      +  ON event#toggling1=0 DO mem1 1 ENDON 
      +  ON event#toggling1=1 DO mem1 0 ENDON 
      +  ON Rules#Timer=1 DO Backlog var1 0; RuleTimer1 70; Power1 0 ENDON 
      +  ON event#temp DO Backlog var1 1; RuleTimer1 70; event ctrl_ready=1; event temp_demand=%value% ENDON 
      +  ON event#ctrl_ready>%mem1% DO var1 0 ENDON 
      +  ON event#temp_demand>%mem2% DO Power1 0 ENDON 
      +  ON event#temp_demand<%mem3% DO Power1 %var1% ENDON
      +

      Tests:

      • Push the button1. The thermostat changes to ENABLED (mem1=1)
      • on console: event temp=20 (now the system receives like a tele message from temperature sensor) and will turn on the relay1 (to heat)
      • on console: event temp=26 (the thermostat turn off the heater)
      • on console: event temp=22 (the thermostat turn on the heater)
      • wait more than a minute without using the event temp and the thermostat will turn off as there is no temperature value (like a sensor error or disconnection)
      • will resume when using again the event temp
      • console mem1 0, DISABLED, console mem1 1, ENABLED

      Timers:

      • With the above the timers can be used to control mem1 and add a schedule to when the thermostat will be enabled
        Rule2 ON Clock#Timer=1 DO mem1 1 ENDON ON Clock#Timer=2 DO mem1 0 ENDON

      Solar heater control~

      In a swimming pool, a filter pump and a solar panel is installed. When the sun is shining, the pump should push water through the solar panel, to heat the pool. When it's night or cloudy, the pump should be off, to avoid cooling the pool water through the solar panel. The pump is controlled by a Sonoff TH10 with 2x DS18B20 sensors connected.

      3 rules:

      • Pump should start when solar panel is more than 2 deg warmer than the pool water
      • Pump should stop when solar panel is less than 1 deg warmer than the pool water
      • Pump should not start if the solar panel is below 25 deg Celsius.

      t1: pool temp
      t2: panel temp
      var1: in valid panel temp range?
      var2: off threshold temp for panel
      var3: on threshold temp for panel
      mem3: lowest valid panel temp

      mem3 25
      +
      rule1
      +  ON DS18B20-1#temperature DO event t1=%value% ENDON
      +  ON DS18B20-2#temperature DO event t2=%value% ENDON
      +  ON event#t2>%mem3% DO var1 1 ENDON
      +  ON event#t2<=%mem3% DO var1 0 ENDON
      +  ON event#t1 DO Backlog var2 %value%; add2 1 ENDON
      +  ON event#t1 DO Backlog var3 %value%; add3 2 ENDON
      +  ON event#t2>%var3% DO Power1 %var1% ENDON
      +  ON event#t2<%var2% DO Power1 0 ENDON
      +

      To test the rule without having the sensors in place, simply enter the events for t1 and t2 in the console:
      Backlog event t1=21;event t2=30

      And watch the relay turn on and off based on the values.

      Please note that this example does not support manual override or handles missing sensor data.


      Energy Saving Switch~

      Example of a switch controlling a light with a condition of a required amount of lux.

      When the switch is on, the light will turn on but only when you have less than 100 lux in that room. While if the switch is off the light will be off.

      Rule1
      +  ON switch1#state=1 DO var1 100 ENDON
      +  ON switch1#state=0 DO Backlog var1 0; Power1 off ENDON
      +  ON APDS9960#Ambient<%var1% DO Power1 on ENDON
      +

      Use of variables and tele- in Domoticz~

      Using variables allows for storing sensor results to be used in composing a single HA message like used with Domoticz. To prevent flooding Domoticz with messages we only want to send a message at TelePeriod time. This is achieved by prefixing the <SensorName> with the label tele-. This example will use a variable storing the temperature to be used together with humidity in one Domoticz MQTT message.

      • Domoticz configured with a virtual sensor Temp+Hum using Idx 134

      Rule

      Rule
      +  ON tele-am2301-12#temperature DO var1 %value% ENDON
      +  ON tele-am2301-12#humidity DO publish domoticz/in {"idx":134,"svalue":"%var1%;%value%;1"} ENDON
      +

      Result - As a result of the tele- prefix the rules will be checked at TelePeriod time for sensor AM2301-12 Temperature and Humidity. The first rule will use the Temperature stored in %value% and save it in %var1% for future use. The second rule will use the Humidity stored in %value% and the Temperature stored in %var1% to compose a single MQTT message suitable for Domoticz.


      Publish Maximum Value from sensor in a time period~

      This rule stores the sensor value in var1. When the current value is greater than the previous, var1 is updated. If the current value is smaller, var1 remains unchanged. Then every minute, var1 is published and reset. This results in publication of the maximum/peak value for the last minute. Useful, for example in a decibel meter, when the peak value is important, rather than the value that is occurring by chance at normal telemetry time.

      This example uses Analog Range mode (trigger analog#range) which is a scaled output value (the raw analog value would be analog#a0). On ESP32 which supports multiple ADC inputs, the ADC index must be appended such as analog#range1 for ADC#1.

      Rule
      +  ON analog#range>%var1% DO VAR1 %value% ENDON
      +  ON Time#Minute DO Backlog publish shed/tele/maxdb %var1%; var1 0 ENDON
      +
      +Rule 1 1
      +

      RF Repeater / IR Repeater~

      In some applications, an RF-Repeater may come in handy to increase the range of RF based devices. We need to use RF receiver and RF transmitter modules with tasmota powered controllers. The following rule looks for data received by the RF receiver and re transmits the same over the transmitter.

      Rule1
      +  on RfReceived#data do RfSend {"Data":%value%,"Bits":24,"Protocol":1,"Pulse":454} endon
      +
      Enable it with Rule1 1

      A similar concept can also work for IR- Repeater. Connect IR receiver module and IR trnasmitter to Tasmotized device and the following rule retransmits any data over IR

      Rule1
      +  on IrReceived#Data do IRsend {"Protocol":"NEC","Bits":32,"Data":%value%} endon
      +
      Enable it with Rule1 1

      The only catch is that the protocol needs to be setup in the rule. Most likely this can be taken care of by using a more complex rule maybe using variables. Would update in future


      \ No newline at end of file diff --git a/SDS011/index.html b/SDS011/index.html new file mode 100644 index 0000000000..bc879cfdc8 --- /dev/null +++ b/SDS011/index.html @@ -0,0 +1,7 @@ + SDS011 air quality sensor - Tasmota
      Skip to content

      SDS011 air quality sensor~

      This feature is included only in tasmota-sensors and tasmota32 binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_NOVA_SDS 
      +#define USE_NOVA_SDS                    // Add support for SDS011 and SDS021 particle concentration sensor (+1k5 code)
      +  #define STARTING_OFFSET      30       // Turn on NovaSDS XX-seconds before tele_period is reached    
      +#endif
      +

      Nova PM Sensor SDS011 can measure the air quality very accurately by measuring air particles or dust based on laser detection.

      Specifications:

      • Voltage: 4.7 - 5.3V DC (5V DC with <20mV ripple advised)
      • Power consumption: 70mA ±10mA (in operation), <4mA (laser and fan sleep mode)
      • Measuring range: 0.0-999.9 μg/m3
      • PM2.5 and PM10 output

      See manufacturer site for more information.

      Configuration~

      Wiring~

      SDS011 ESP
      GND GND
      5V 5V
      TX GPIO RX
      RX GPIO TX

      Tasmota Settings~

      In the Configuration -> Configure Module page assign: 1. GPIO RX to SDS0X1 Tx (101) 2. GPIO TX to SDS0X1 Rx (70)

      screenshot-2021-10-14_08-36-17

      To extend the lifetime of the sensor, you can use the command Sensor20 in association with setting TelePeriod for how often you want the fan to run and the sensor to be polled.

      Tasmota Main~

      After reboot of the device the displays the SDS011 measurements.

      OpenHab~

      Number Dust_Sensor_2_5 "PM 2.5 [%.2f µg/m³]" <door> (Dust) {mqtt="<[mosquitto:tele/dust/SENSOR:state:JSONPATH($.SDS0X1['PM2.5'])]"}
      +Number Dust_Sensor_10  "PM 10 [%.2f µg/m³]" <door> (Dust) {mqtt="<[mosquitto:tele/dust/SENSOR:state:JSONPATH($.SDS0X1['PM10'])]"}
      +
      \ No newline at end of file diff --git a/SHT30/index.html b/SHT30/index.html new file mode 100644 index 0000000000..0eb9d79d79 --- /dev/null +++ b/SHT30/index.html @@ -0,0 +1,4 @@ + SHT30 temperature sensor - Tasmota
      Skip to content

      SHT30 temperature sensor~

      This feature is included only in tasmota-sensors and tasmota32 binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_SHT3X 
      +#define USE_SHT3X           // [I2cDriver15] Enable SHT3x (I2C address 0x44 or 0x45) or SHTC3 (I2C address 0x70) sensor (+0k7 code)
      +#endif
      +

      Wemos SHT30 Shield High Precision Humidity & Temperature (I2C) sensor

      From the Wemos SHT30 shield specs the SDA pin on the SHT30 shield is connected to D2 and the SCL pin to D1.

      Configuration~

      As a default the SHT30 uses I2C address 0x45 and is user changeable by short-circuiting the two soldering pads at the lower left-side of the sensor (see image below marked green).

      Also if no other I2C devices are connected to the Wemos it might be wise to short-circuit the three soldering pads at the upper left-side of the sensor (see image below marked red). This connects the pull-up resistors for I2C and biases the SCL and SDA pins to VCC. This is to avoid possible false detections of other sensors like VEML6070.

      Note: On the v2.1.0 board these pads are on the rear.

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      • D2 GPIO4 to I2C SDA
      • D1 GPIO5 to I2C SCL

      wemos_sht30_config_marked

      After reboot of the device the temperature, humidity and dew point are displayed.

      wemos_sht30_main_marked

      Breakout Boards~

      Three versions are known to exist, they all share the same pinout. Lolin is the new name for Wemos going forward.

      • v.1.0.0 - original design (retired)
      • v.2.0.0 - detachable design
      • v.2.1.0 - detachable design with TFT I2C connector

      wemos_sht30_shield_v2.0.1

      \ No newline at end of file diff --git a/SK6812/index.html b/SK6812/index.html new file mode 100644 index 0000000000..90db1e618e --- /dev/null +++ b/SK6812/index.html @@ -0,0 +1,3 @@ + SK6812 RGBW Addressable LEDs - Tasmota
      Skip to content

      SK6812 RGBW Addressable LEDs~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #define USE_WS2812_HARDWARE  NEO_HW_SK6812     
      +#define USE_WS2812_CTYPE     NEO_GRBW           // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW)
      +

      Wiring with a level shifter is required. Look at WS2812B LEDs article for instructions.

      Tasmota Settings~

      In the Configuration -> Configure Module page, select the following:

      1. GPIOx to WS2812

      Tasmota Main~

      After reboot of the device the color sliders and toggle button are displayed to control the LED.

      Set number of pixels used on your strip with Pixels x command

      Select whether use white in separate channel or blended with RGB colors.

      Configuration was done on a round 7 LED board and an ESP-01S Addressable LED adapter

      \ No newline at end of file diff --git a/SPS30/index.html b/SPS30/index.html new file mode 100644 index 0000000000..53209932ac --- /dev/null +++ b/SPS30/index.html @@ -0,0 +1,2 @@ + SPS30 Particulate sensor - Tasmota
      Skip to content

      SPS30 Particulate sensor~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #define USE_SPS30         // [I2cDriver30] Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code)
      +

      Device information~

      The Sensirion SPS30 is a laser based, fan driven, MCERTS-certified particulate matter (PM) sensor with a I²C and UART interface (datasheet). The Tasmota driver implements the I²C interface.

      Mass Concentration Measurements~

      The device exposes the following data:

      • Volumetric concentration (μg/m³):
      • PM1.0, PM2.5, PM4 and PM10
      • Number concentration (binning, #/cm³):
      • PM0.5, PM1.0, PM2.5, PM4 and PM10
      • Typical particle size (μm)

      Sampling~

      The module has a minimum sampling interval of 1 second in continuous mode (default).

      Automatic Cleaning~

      It implements an automatic fan cleaning routine, which sets the built-in fan speed to maximum for 10 seconds to clear out the detector chamber and accumulated dust on the fan blades. The default cleaning interval is 1 week of interrupted use, and resetting the sensor power also resets the built-in time counter for this. The current implementation does not support disabling this or setting a custom interval.

      Configuration~

      The sensor has a 5 pin JST ZHR type connector, with a 1.5mm pitch (connector, datasheet). Pin5 (SEL/Interface Select) must be shorted to Pin4/Ground to enable the I²C interface.

      The sensor and fan components require 5V VDD, so a 5V capable board (e.g. Wemos D1) or external power must be supplied.

      Finding appropriate connectors/cables for this module are somewhat cumbersome, SparkFun sells them with handy breadboard friendly breakout cables.

      SPS30 pinout

      Pin SPS30 ESP Comment
      1 VDD + 5V ± 10%
      2 SDA GPIOx 5V and 3.3V compatible
      3 SCL GPIOy 5V and 3.3V compatible
      4 SEL - Pull to GRD to enable I²C
      5 GND -

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      • GPIOx to I2C SDA
      • GPIOy to I2C SCL

      After saving the board will reboot and the particulate measurements will be displayed on the main page.

      \ No newline at end of file diff --git a/Safeboot/index.html b/Safeboot/index.html new file mode 100644 index 0000000000..598bbc0cb6 --- /dev/null +++ b/Safeboot/index.html @@ -0,0 +1 @@ + Safeboot Partition Layout - Tasmota
      Skip to content

      Safeboot Partition Layout ~

      This page is specific to ESP32 and variants (not applicable to ESP8266)

      This feature was introduced in Tasmota v12

      Safeboot partition layout is a new feature in Tasmota v12 that optimizes the use of Flash memory and allows for bigger filesystems and bigger firmware sizes.

      Flash_layout_original Flash_layout_safeboot Flash_layout_safeboot_alt

      Before v12~

      Tasmota used a standard partition layout consisting of 2 firmware partitions of equal sizes. One partition is active and is booted from, while the other partition receives the new code when an OTA (Over The Air) update occurs.

      This scheme is standard in ESP32 Arduino. Its main advantage is that it is very safe. If a power failure occurs during the OTA update, the device is not bricked and reboots on the untouched version. The main drawback is that it consumes a lot of flash space, enough to contains twice the firmware size.

      Example, standard Tasmota layout for 4MB flash:

      • a fixed size 64KB system area
      • 2x partitions of 1856KB each to receive Tasmota code
      • a filesystem of 320KB

      Flash_layout_original

      Introducing Safeboot~

      We introduced a new variant of Tasmota called "Safeboot". It contains a minimal version of Tasmota used only for OTA, and a normal firmware. The main advantage is that it provides 1024KB of additional storage for firmware and/or filesystem.

      Example of new partition layout since v12:

      • a fixed size 64KB system area
      • a Safeboot partition of 832KB
      • 1x partition of 2880KB to receive Tasmota code
      • a filesystem of 320KB

      Flash_layout_safeboot

      An alternate partition scheme is used in Sonoff Zigbee Bridge Pro:

      • a fixed size 64KB system area
      • a Safeboot partition of 832KB
      • 1x partition of 1856KB to receive Tasmota code
      • a filesystem of 1344KB

      Flash_layout_safeboot_alt

      Note: the Safeboot firmware is a reduced version of Tasmota containing only what's required for OTA updates (Web UI, MQTT, TLS...). However it does not save settings nor support initial Wi-Fi configuration.

      \ No newline at end of file diff --git a/Scripting-Cookbook/index.html b/Scripting-Cookbook/index.html new file mode 100644 index 0000000000..1017d4546c --- /dev/null +++ b/Scripting-Cookbook/index.html @@ -0,0 +1,11 @@ + + + + + + + + +Redirecting... + + diff --git a/Scripting-Language/index.html b/Scripting-Language/index.html new file mode 100644 index 0000000000..f22a8d74c1 --- /dev/null +++ b/Scripting-Language/index.html @@ -0,0 +1,1501 @@ + Scripting Language - Tasmota
      Skip to content

      Scripting~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_SCRIPT
      +#define USE_SCRIPT  // adds about 17k flash size, variable ram size
      +#endif
      +#ifdef USE_RULES
      +#undef USE_RULES
      +#endif  
      +

      Additional features are enabled by adding the following #define compiler directive parameters and then compiling the firmware. These parameters are explained further below in the article.

      Feature Description
      USE_BUTTON_EVENT enable >b section (detect button state changes)
      USE_SCRIPT_JSON_EXPORT enable >J section (publish JSON payload on TelePeriod)
      USE_SCRIPT_SUB_COMMAND enables invoking named script subroutines via the Console or MQTT
      USE_SCRIPT_HUE enable >H section (Alexa Hue emulation)
      USE_HOMEKIT enable >h section (Siri Homekit support (ESP32 only),
      define must be given in platform_override see below)
      USE_SCRIPT_STATUS enable >U section (receive JSON payloads from cmd status)
      SCRIPT_POWER_SECTION enable >P section (execute on power changes)
      SUPPORT_MQTT_EVENT enables support for subscribe unsubscribe
      USE_SENDMAIL enable >m section and support for sending e-mail
      (on ESP32 you must add USE_ESP32MAIL)
      USE_SCRIPT_WEB_DISPLAY enable >W section (modify web UI)
      SCRIPT_FULL_WEBPAGE enable >w section (separate full web page and webserver)
      USE_TOUCH_BUTTONS enable virtual touch button support with touch displays
      USE_WEBSEND_RESPONSE enable receiving the response of WebSend and WebQuery commands (received in section >E)
      SCRIPT_STRIP_COMMENTS enables stripping comments when attempting to paste a script that is too large to fit
      USE_ANGLE_FUNC add sin(x),acos(x) and sqrt(x) e.g. to allow calculation of horizontal cylinder volume
      USE_SCRIPT_FATFS_EXT enables additional FS commands
      USE_WEBCAM enables support ESP32 Webcam which is controlled by scripter cmds
      USE_FACE_DETECT enables face detecting in ESP32 Webcam
      USE_SCRIPT_TASK enables multitasking Task in ESP32
      USE_LVGL enables support for LVGL
      USE_SCRIPT_GLOBVARS enables global variables and >G section
      USE_SML_M enables Smart Meter Interface
      SML_REPLACE_VARS enables possibility to replace the lines from the (SML) descriptor with Vars
      NO_USE_SML_SCRIPT_CMD disables SML script cmds
      USE_SCRIPT_I2C enables I2C support
      USE_SCRIPT_SERIAL enables support for serial io cmds
      USE_LVGL enables support for LVGL
      USE_SCRIPT_TIMER enables up to 4 Arduino timers (so called tickers)
      SCRIPT_GET_HTTPS_JP enables reading HTTPS JSON WEB Pages (e.g. Tesla Powerwall)
      LARGE_ARRAYS enables arrays of up to 1000 entries instead of max 127
      SCRIPT_LARGE_VNBUFF enables to use 4096 in stead of 256 bytes buffer for variable names
      USE_GOOGLE_CHARTS enables definition of google charts within web section
      USE_FEXTRACT enables array extraction from database fxt(...), fxto() and tso(), tsn(), cts(), s2t() functions
      USE_SCRIPT_SPI enables support for SPI interface
      USE_DSIPLAY_DUMP enables to show epaper screen as BMP image in >w section
      TS_FLOAT may be define as double to use double precision numbers (uses double RAM memory and is slower)

      Scripting Language for Tasmota is an alternative to Tasmota Rules. For ESP32 builds it is recommended to use Berry

      To enter a script, go to Consoles -> Edit Script in the Tasmota web UI menu (for version before 9.4, go to Configuration -> Edit script)

      To save code space almost no error messages are provided. However it is taken care of that at least it should not crash on syntax errors.

      Features~

      • Up to 50 variables (45 numeric and 5 strings - this may be changed by setting a compilation #define directive)
      • Freely definable variable names (all variable names are intentionally case sensitive)
      • Nested if,then,else up to a level of 8
      • Math operators +,-,*,/,%,&,|,^,<<,>>
      • All operators may be used in the op= form, e.g., +=
      • Comparison operators ==,!=,>,>=,<,<=
      • and , or support
      • Hexadecimal numbers with prefix 0x are supported
      • Strings support + and += operators
      • Support for \n \r regular expressions on strings
      • String comparison ==, !=
      • String size is 19 characters (default). This can be increased or decreased by the optional parameter on the D section definition

      Script Interpreter~

      • Execution is strictly sequential, line by line
      • Evaluation is left to right with optional brackets
      • All numbers are float, e.g., temp=hum*(100/37.5)+temp-(timer*hum%10)
      • No spaces are allowed between math operators
      • Comments start with ;

      Script buffer size~

      the script language normally shares script buffer with rules buffer which is 1536 chars. with below options script buffer size may be expanded. PVARS is size for permanent vars.

      Feature ESP ESP32 PVARS remarks
      fallback 1536 1536 50 no longer supported
      compression (default) 2560 2560 50 actual compression rate may vary
      #define USE_UFILESYS
      #define UFSYS_SIZE S
      S<=8192 S<=16384 1536 ESP must use 4M Flash use linker option -Wl,-Teagle.flash.4m2m.ld or SDCARD
      ESP32 can use any linker file, size of Filesystem depends on linker file
      #define EEP_SCRIPT_SIZE S
      #define USE_EEPROM
      #define USE_24C256
      S<=8192 S<=16384 1536 for hardware eeprom only
      #define EEP_SCRIPT_SIZE 8192
      #define USE_EEPROM
      S=8192 not supported 1536 script may be lost on OTA and serial flash, not on restart

      most useful definition for larger scripts would be

      ESP8266~

      with 1M flash only default compressed mode should be used (or an SDCARD)
      a special mode can be enabled for 8192 chars by defining #define USE_EEPROM, #define EEP_SCRIPT_SIZE 8192
      however this has some side effects. the script may be deleted on firware OTA or serial update and may have to be reinstalled after update.

      with 4M Flash best mode would be
      #define USE_UFILESYS
      with linker file "eagle.flash.4m2m.ld"

      ESP32~

      with all linker files
      #define USE_UFILESYS

      script init error codes~

      after initialization the script reports some info in the console e.g:
      20:21:28.259 Script: nv=51, tv=13, vns=279, vmem=3305, smem=4096
      nv = number of used variables in total (numeric and strings)
      tv = number of used string variables
      vns = total size of name strings in bytes (may not exceed 255) or #define SCRIPT_LARGE_VNBUFF extents the size to 4095 vmem = used heap ram by the script (psram if available)
      smem = used script (text) memory (psram if available)

      if the script init fails an error code is reported:
      -4 = not enough memory
      -5 = variable name length too long in total
      -6 = too many arrays defined
      -7 = not enough memory

      number of variables is only limited by RAM. you will probably get a memory error when you define to many variables. you may increase the number of allowed array and the maximum string size defines in user_config_override
      defaults and override defines:
      Number of filters (arrays) = 5 (override #define MAXFILT)
      Max string size = 20 (increase with >D size up to default default 48) (override #define SCRIPT_MAXSSIZE)

      Optional external editor~

      you may use a special external editor with syntax highlighting to edit the scripts. (mac and pc) you may use any number of comments and indents to make it better readable. then with cmd r the script is transferred to the ESP and immediately started. (all comments and indents are removed before transferring) see further info and download here

      Console Commands~

      script <n> : 0 = switch script off; 1 = switch script on
      script ><cmdline> execute
      - Can be used to set variables, e.g., script >mintmp=15
      - Multiple statements can be specified by separating each with a semicolon, e.g. script >mintmp=15;maxtemp=40

      script?<var> queries a script variable var

      • The script itself can't be specified because the size would not fit the MQTT buffers

      Script Sections~

      Section descriptors (e.g., >E) are case sensitive
      a valid script must start with >D in the first line
      >D ssize
      ssize = optional max string size (default=19, max=48 unless increased with #define SCRIPT_MAXSSIZE)
      define and init variables here, must be the first section, no other code allowed
      p:vname
      specifies permanent variables. The number of permanent variables is limited by Tasmota rules space (50 bytes) - numeric variables are 4 bytes; string variables are one byte longer than the length of string
      t:vname
      specifies countdown timers, if >0 they are decremented in seconds until zero is reached. see example below
      i:vname
      specifies auto increment counters if =0 (in seconds)
      g:vname
      specifies global variable which is linked to all global variables with the same definition on all devices in the homenet. when a variable is updated in one device it is instantly updated in all other devices. if a section >G exists it is executed when a variable is updated from another device (this is done via UDP-multicast, so not always reliable) I:vname
      specifies an integer 32 bit variable instead of float. (limited support) integer constants must be preceeded by '#' m:vname
      specifies a median filter variable with 5 entries (for elimination of outliers)
      M:vname
      specifies a moving average filter variable with 8 entries (for smoothing data, should be also used to define arrays)
      (max 5 filters in total m+M) optional another filter length (1..127) can be given after the definition.
      Filter vars can be accessed also in indexed mode vname[x] (x = 1..N, x = 0 returns current array index pointer (may be set also), x = -1 returns array length, x = -2 returns array average) Using this filter, vars can be used as arrays, #define LARGE_ARRAYS allows for arrays up to 1000 entries
      array may also be permanent by specifying an extra :p
      m:p:vname defines a permanent array. Keep in mind however that in 1M Flash standard configurations you only have 50 bytes permanent storage which stands for a maximum of 12 numbers. (see list above for permanent storage in other configurations)
      arrays may also be preset in auto increment mode array=X sets the value at index array[0] and increments the index by 1.
      array = {x y z} sets 3 values in an array from index array[0]

      Tip

      Keep variable names as short as possible. The length of all variable names taken together may not exceed 256 characters.
      Memory is dynamically allocated as a result of the D section.
      Copying a string to a number or reverse is supported

      >B
      executed on BOOT time before sensors are initialized and on save script

      >BS
      executed on BOOT time after sensors are initialized

      >E
      Executed when a Tasmota MQTT RESULT message is received, e.g., on POWER change. Also Zigbee reports to this section.

      >F
      Executed every 100 ms

      >S
      Executed every second

      >R
      Executed on restart. p vars are saved automatically after this call

      >T
      Executed at least at TelePeriod time (SENSOR and STATE) but mostly faster up to every 100 ms, only put tele- vars in this section
      Remark: JSON variable names (like all others) may not contain math operators like - , you should set SetOption64 1 to replace - (dash) with _ (underscore). Zigbee sensors will not report to this section, use E instead.

      >H
      Alexa Hue interface (up to 32 virtual hue devices) (example)
      device,type,onVars
      Remark: hue values have a range from 0-65535. Divide by 182 to assign HSBcolors hue values.

      device device name
      type device type - E = extended color; S = switch
      onVars assign Hue "on" extended color parameters for hue, saturation, brightness, and color temperature (hue,sat,bri,ct) to scripter variables

      Example

      lamp1,E,on=pwr1,hue=hue1,sat=sat1,bri=bri1,ct=ct1

      >h passcode
      Siri Homekit interface (up to 16 virtual Homekit devices)
      passcode = 111-11-111 keep this format, numbers 0-9
      name,type,opt,var1,var2...

      name device name (max 23 characters)
      type device type (HAP_CID)
      - 7 = outlet, on/off
      - 5 = light, on/off,hue,sat,bri
      - 10 = sensor

      opt sensor type
      - 0 = Temperature,val
      - 1 = Humidity,val
      - 2 = Light level,val
      - 3 = Battery status,level,low battery,charging
      - 4 = Ambient light level with extended range -10000,+10000
      - 5 = Contact Sensor (switch)

      var1 ... variable name (max 11 characters) the variables denote scripting variables that need to be set by script
      the special variables
      @px x (1..9) directly set, read power states e.g. relays
      @sx x (1..9) directly read switch state
      @bx x (1..9) directly read button state

      Example

      >h 111-11-111
      outlet,7,0,@p1 lamp1,5,0,pwr,hue,sat,bri
      temperature,10,0,tval

      a restart is required after modification of descriptor!
      by faulty parameters the homekit dataset may get corrupted
      to reset the homekit dataset completely type in console script>hki(89)

      compilation:

      needs to add in linker to

      build_flags
      -DUSE_HOMEKIT

      lib_extra_dirs
      lib/libesp32_div

      >U
      JSON messages from cmd status arrive here

      >C
      HTML messages arrive here (on web user io event, (if defined USE_HTML_CALLBACK))

      >G
      global variable updated section

      >P
      any power change triggers here (if defined SCRIPT_POWER_SECTION)

      >jp
      https webpage json parse arrives here

      >ti1
      >ti2
      >ti3
      >ti4
      ticker callback after timer expiration

      >b (note lower case)
      executed on button state change

      bt[x]
      read button state (x = 1.. MAX_KEYS)

      Example

      >D
      +tmp=0
      +>b
      +tmp=bt[1]
      +if tmp==0  
      +then  
      +print falling edge of button1  
      +endif  
      +if tmp==1  
      +then  
      +print rising edge of button1  
      +endif
      +

      >J
      The lines in this section are published via MQTT in a JSON payload on TelePeriod. Requires compiling with #define USE_SCRIPT_JSON_EXPORT.

      >W
      The lines in this section are displayed in the web UI main page. Requires compiling with #define USE_SCRIPT_WEB_DISPLAY.

      You may put any html code here.

      • Variables may be substituted using %var%
      • HTML statements are displayed in the sensor section of the main page
      • HTML statements preceded with a @ are displayed at the top of the page
      • HTML statements preceded with a $ are displayed in the main section
      • USER IO elements are displayed at the top of the page

      optionally these sections may be used
      >WS
      - HTML statements are displayed in the sensor section of the main page

      >WM
      - HTML statements are displayed in the main section of the main page

      for next loops are supported to repeat HTML code (precede with % char) %for var from to inc %next

      but this method is preferred: script subroutines may be called sub=name of subroutine, like normal subroutines %=#sub in this subroutine a web line may be sent by wcs (see below) thus allowing dynamic HTML pages

      =#sub(x) in any position of webline calls subroutine. this allows inserting content

      insa(array) in any position insert all elements from an array comma separated

      %/file calls a file from the file system and send its content to browser. in this file any cmds may apply.

      A web user interface may be generated containing any of the following elements:

      remark: state variable names used for IO in the web interface may not contain an underscore.
      Button:
      bu(vn txt1 txt2) (up to 4 buttons may be defined in one row)
      vn = name of variable to hold button state
      txt1 = text of ON state of button
      txt2 = text of OFF state of button

      Pulldown:
      pd(vn label (xs) txt1 txt2 ... txtn)
      vn = name of variable to hold selected state
      label = label text
      xs = optional xs (default 200) txt1 = text of 1. entry
      txt2 = text of 2. entry and so on

      Radio button:
      rb(vn label (xs) txt1 txt2 ... txtn)
      vn = name of variable to hold selected state
      label = label text
      xs = optional xs (default 200) txt1 = text of 1. entry
      txt2 = text of 2. entry and so on

      Checkbox:
      ck(vn txt (xs))
      vn = name of variable to hold checkbox state
      txt = label text
      xs = optional xs (default 200)

      Slider:
      sl(min max vn ltxt mtxt rtxt)
      min = slider minimum value
      max = slider maximum value
      vn = name of variable to hold slider value
      ltxt = label left of slider
      mtxt = label middle of slider
      rtxt = label right of slider

      Text Input:
      tx(vn lbl (xs) (type min max))
      vn = name of string variable to hold text state
      lbl = label text
      xs = optional xs (default 200)
      type min max = optional strings type = e.g "datetime-local" for date+time selector, min, max = date-time min max range

      Time Input:
      tm(vn lbl (xs))
      vn = name of number variable to hold time HHMM as number e.g. 1900 means 19:00
      lbl = label text
      xs = optional xs (default 70)

      Number Input:
      nm(min max step vn txt (xs) (prec))
      min = number minimum value
      max = number maximum value
      step = number step value for up/down arrows
      vn = name of number variable to hold number
      txt = label text
      xs = optional xs (default 200)
      prec = optional number precision (default 1)

      special html options:
      so(flags)
      WSO_NOCENTER = 1 force elements not centered
      WSO_NODIV = 2 force elements not in extra \<div>
      WSO_FORCEPLAIN = 4 send line in plain (no table elements)
      WSO_FORCEMAIN = 8 send lines in main mode ($ mode)

      Google Charts:
      google chart support requires arrays and to make sense also permanent arrays. Therefore on 4M Flash Systems the use of USE_UFILESYS is recommended while on 1 M Flash Systems the special EEPROM mode should be used (see above). other options may also be needed like LARGE_ARRAYS

      draws a google chart with up to 4 data sets per chart
      gc(T (size) array1 ... array4 "name" "label1" ... "label4" "entrylabels" "header" {"maxy1"} {"maxy2"})
      T = type
      - b=barchart
      - c=columnchart
      - cs=columnchart stacked - C=combochart
      - p=piechart
      - l=linechart up to 4 lines with same scaling
      - l2=linechart with exactly 2 lines and 2 y scales (must be given at end)
      - lf2 like above but with splined lines
      - h=histogram
      - t=data table
      - g=simple gauges (must give extra 3 vars after header, yellow start, red start, maxval)
      - T=Timeline (special type arrays contains start,stop pairs in minutes timeofday)

      b,l,h type may have the '2' option to specify exactly 2 arrays with 2 y scales given at the end of parameter list.

      a very individual chart may be specified by splitting the chart definition and inserting the chart options directly see example below

      size = optional size, allows to use only part of an array, must be lower then array size

      array = up to 4 arrays of data
      name = name of chart
      label = label for up to the 4 datasets in chart
      entrylabel = labels of each x axis entry separated by '|' char
      ("cntN" starts numbering entries with the number N an optional /X generates numbers divided by X. Produce labels that cycle through the array indexes, starting with the number N. For instance, "cnt6" with an array of length 8 will produce the labels 6|7|0|1|2|3|4|5| Using "cntN/X" will then divide the numeric value of the label, so "cnt6/2" with an array of length 8 will produce the labels 3|3|0|0|1|1|2|2|) ("wdh: before a week definition generates a week with full hours)
      header = visible header name of chart
      the curve displayed in google chart starts at array index (array[0]) so array index must be set also. thus the displayed curve may be shifted to the desired position by adjusting the array index.

      additionally you have to define the html frame to put the chart in (both lines must be preceded by a $ char) e.g.

      $<div id="chart1"style="width:640px;height:480px;margin:0 auto"></div>
      +  $gc(c array1 array2 "wr" "pwr1" "pwr2" "mo|di|mi|do|fr|sa|so" "Solar feed")

      you may define more then one chart. The charts id is chart1 ... chartN

      very customized chart definition:
      define a chart like above, but add a t to the definition
      this generates a google table from the arrays e.g.:
      &gc(lt array1 array2 "wr" "pwr1" "pwr2" "mo|di|mi|do|fr|sa|so")

      then define the options for the graph as from the doku of google e.g.:
      $var options = {
      $vAxes:{0:{maxValue:40,title:'Außentemperatur'},1:{maxValue:60,title:'Solarspeicher'}},
      $series:{0:{targetAxisIndex:0},1:{targetAxisIndex:1}},
      $hAxis: {title: 'Wochenverlauf'},
      $};
      then gc(e) closes the definition
      $gc(e)

      >w ButtonLabel generates a button with the name "ButtonLabel" in Tasmota main menu.
      Clicking this button displays a web page with the HTML data of this section. all cmds like in >W apply here. these lines are refreshed frequently to show e.g. sensor values. lines preceded by $ are static and not refreshed and displayed below lines without $.
      this option also enables a full webserver interface when USE_UFILESYS is active.
      you may display files from the flash or SD filesystem by specifying the url: IP/ufs/path . (supported files: *.jpg, *.html, *.txt)
      >w1 >w2 >w3 >w4 >w5 >w6 some as above >w
      Requires compiling with #define SCRIPT_FULL_WEBPAGE.

      >M
      Smart Meter Interface

      If a variable does not exist, ??? is displayed for commands

      If a Tasmota SENSOR or STATUS or RESULT message is not generated or a Var does not exist the destination variable is NOT updated.

      Special Variables~

      (read only)
      upsecs = seconds since start
      uptime = minutes since start
      time = minutes since midnight
      sunrise = sunrise minutes since midnight
      sunset = sunset minutes since midnight
      tper = TelePeriod (may be set also)
      cbs = command text buffer size for tasmota cmds (default 256) (may be set also)
      tstamp = timestamp (local date and time)
      topic = mqtt topic
      gtopic = mqtt group topic
      lip = local ip as string
      luip = udp ip as string (from updating device when USE_SCRIPT_GLOBVARS defined)
      prefixn = prefix n = 1-3
      frnm = friendly name
      dvnm = device name
      pwr[x] = power state (x = 1..N)
      npwr = number of tasmota power devices
      pc[x] = pulse counter value (x = 1..4)
      tbut[x] = touch screen button state (x = 1..N)
      sw[x] = switch state (x = 0..N) (Switch1 = sw[0])
      bt[x] = button state (x = 1..N) only valid in section b (if defined USE_BUTTON_EVENT)
      pin[x] = GPIO pin level (x = 0..16)
      pn[x] = GPIO for sensor code x. 99 if none
      pd[x] = defined sensor for GPIO x. 999 if none
      adc(fac (pin)) = get adc value (on ESP32 can select pin) fac is number of averaged samples (power of 2: 0..7)
      sht[x] = shutter position (x = 1..N) (if defined USE_SHUTTER)
      gtmp = global temperature
      ghum = global humidity
      gprs = global pressure
      pow(x y) = calculates exponential powers x^y (imprecise version only)
      med(n x) = calculates a 5 value median filter of x (2 filters possible n=0,1)
      int(x) = gets the integer part of x (like floor)
      floor(x) = gets the integer part of x
      ceil(x) = gets the integer + 1 part of x
      round(x) = round to nearest integer x
      i(x) = convert float x to integer
      f(x) = convert integer x to float
      hn(x) = converts x (0..255) to a hex nibble string
      hni(x) = converts integer x (0..255) to a hex nibble string
      hx(x) = converts x (0..4294967295, 32-bit) to a hex string
      hxi(x) = converts integer x (0..4294967295, 32-bit) to a hex string
      hd("hstr") = converts hex number string to a decimal number
      af(array index) = converts 4 bytes of an array at index index to float number
      as(array) = sort array
      sas(index) = sort string array (is, is1, is2, index = 1,2,3)
      hf("hstr") = converts hex float number string to a decimal number
      hf("hstr" r) = converts hex float number string (reverse byte order) to a decimal number
      st(svar c n) or = st(svar 'c' n)string token - retrieve the nth element of svar delimited by c,
      ins(s1 s2) = check if string s2 is contained in string s1, return -1 if not contained or position of contained string
      sl(svar) = gets the length of a string
      asc(svar) = gets the binary value of 1. char of a string
      sb(svar p n) = gets a substring from svar at position p (if p<0 counts from end) and length n
      is(num "string1|string2|....|stringn|") = defines a string array optionally preset with immediate strings separated by '|' (this immediate string may be up to 255 chars long) num = 0 read only string array, num > 0 number of elements in read write string array
      is[index] = gets string index from string array, if read-write also write string of index
      is1(..), is2(...) string array see above
      is1[x], is2[x] string array see above
      rr() = returns the reset reason of last restart (as string)
      s2hms(S), converts seconds to HH:MM:SS string
      sin(x) = calculates the sinus(x) (if defined USE_ANGLE_FUNC)
      cos(x) = calculates the cosinus(x) (if defined USE_ANGLE_FUNC)
      acos(x) = calculates the acos(x) (if defined USE_ANGLE_FUNC)
      sqrt(x) = calculates the sqrt(x) (if defined USE_ANGLE_FUNC)
      abs(x) = calculates the absolute value of x
      mpt(x) = measure pulse time, x>=0 defines pin to use, -1 returns low pulse time,-2 return high pulse time (if defined USE_ANGLE_FUNC)
      rnd(x) = return a random number between 0 and x, (seed may be set by rnd(-x))
      sf(F) = sets the CPU Frequency (ESP32) to 80,160,240 Mhz, returns current Freq.
      s(x) = explicit conversion from number x to string may be preceded by precision digits e.g. s(2.2x) = use 2 digits before and after decimal point

      I2C support #define USE_SCRIPT_I2C
      ia(AA), ia2(AA) test and set I2C device with address AA (on BUS 1 or 2), returns 1 if device is present
      iw(aa val) , iw1(aa val), iw2(aa val), iw3(aa val)write val to register aa (1..3 bytes), if in aa bit 15 is set no destination register is transfered (needed for some devices), if bit 14 is set byte order is reversed
      ir(aa), ir1(aa), ir2(aa), ir3(aa) read 1..3 bytes from register aa

      Onewire support #define USE_SCRIPT_ONEWIRE
      support for onewire either directly or via serial port with onewire bus driver DS2480B
      ow(SEL <opt PAR>) SEL 0 = init bus with pin number N (if bit 15 ist set, select serial DS2480B, lsb = rec pin, msb = trx pin)
      SEL 1 = reset cmd
      SEL 2 = skip cmd
      SEL 3 = write PAR
      SEL 4 = read
      SEL 5 = reset search cmd
      SEL 6 = search cmd addr index PAR
      SEL 7 = select cmd addr index PAR SEL 8 = select and set bits index PAR
      SEL 9 = select and read word index PAR bit 7 = 0 start, bit 7 = 1 read result
      SEL 10-18 = get byte (1-8) of adress from index PAR
      SEL 99 = delete bus driver

      Serial IO support #define USE_SCRIPT_SERIAL
      so(RXPIN TXPIN BR) open serial port with RXPIN, TXPIN and baud rate BR with 8N1 serial mode (-1 for pin means don't use)
      so(RXPIN TXPIN BR MMM) open serial port with RXPIN, TXPIN and baud rate BR and serial mode e.g 7E2 (all 3 modechars must be specified)
      so(RXPIN TXPIN BR MMM BSIZ) open serial port with RXPIN, TXPIN and baud rate BR and serial mode e.g 7E2 (all 3 modechars must be specified) and serial IRW buffer size
      sc() close serial port
      sw(STR) write the string STR to serial port
      swb(NUM) write the number char code NUM to serial port
      sa() returns number of bytes available on port
      sr() read a string from serial port, all available chars up to string size
      sr(X) read a string from serial port until charcode X, all available chars up to string size or until charcode X
      srb() read a number char code from serial port
      sp() read a number char code from serial port, don't remove it from serial input (peek)
      sra(ARRAY (flags)) fill an array from serial port, if USE_SML_M is enabled and Array size is 8 it is assumed to be a MODBUS request and the checksum is evaluated, if OK 8 is returned, else -2, or if flags is set Modbus response is assumed and checksum is calculated, 0 = standard Modbus, 1 = Rec BMA mode, return -2 on checksum error a

      sra(ARRAY (flags)) fill an array from serial port, if USE_SML_M is enabled and Array size is 8 it is assumed to be a MODBUS request and the checksum is evaluated, if OK 8 is returned, else -2, or if flags is set Modbus response is assumed and ckum is calculated, 0 = standard Modbus, 1 = Rec BMA mode
      swa(ARRAY len (flags)) send len bytes of an array to serial port, if flags is set Modbus cmd is assumed and cksum is calculated, 0 = standard Modbus, 1 = Rec BMA mode
      smw(ADDR MODE NUMBER) send a value with checksum to MODBUS Address, MODE 0 = uint16, 1 = uint32, 3 = float

      SPI IO support #define USE_SCRIPT_SPI
      spi(0 SCLK MOSI MISO) defines a software SPI port with pin numbers used for SCLK, MOSI, MISO.
      spi(0 -1 freq) defines a hardware SPI port with pin numbers defined by Tasmota GPIO definition with bus frequency in Mhz.
      spi(0 -2 freq) defines a hardware SPI port 2 on ESP32 with pin numbers defined by Tasmota GPIO definition.
      spi(1 N GPIO) sets the CS pin with index N (1..4) to pin Nr GPIO.
      spi(2 N ARRAY LEN S) sends and receives an ARRAY with LEN values with S (1..3) (8,16,24 bits) if N==-1 CS is ignored. If S=4, CS is raised after each byte.

      ttget(TNUM SEL) get tasmota timer setting from timer TNUM (1 .. 16)
      SEL:
      0 = time
      1 = time window
      2 = repeat
      3 = days
      4 = device
      5 = power
      6 = mode
      7 = arm
      mqtts = MQTT connection status: 0 = disconnected, >0 = connected
      wbut = button status of watch side button (if defined USE_TTGO_WATCH)
      wdclk = double tapped on display (if defined USE_TTGO_WATCH)
      wtch(sel) = gets state from touch panel sel=0 => touched, sel=1 => x position, sel=2 => y position (if defined USE_TTGO_WATCH)
      slp(time) = sleep time in seconds, pos values => light sleep, neg values => deep sleep (if defined USE_TTGO_WATCH)
      pl("path") = play mp3 audio from filesystem (if defined USE_I2S_AUDIO or USE_TTGO_WATCH or USE_M5STACK_CORE2)
      say("text") = plays specified text to speech (if defined USE_I2S_AUDIO or USE_TTGO_WATCH or USE_M5STACK_CORE2)
      c2ps(sel val) = gets, sets values on ESP32 CORE2 sel=0 green led, sel=1 vibration motor, sel=2,3,4 get touch button state 1,2,3 (if defined USE_M5STACK_CORE2)
      rec(path seconds) = rec n seconds wav audio file from i2s microphone to filesystem path (if defined USE_I2S_AUDIO or USE_M5STACK_CORE2)
      pwmN(-pin freq) = defines a pwm channel N (1..N) with pin Nr and frequency (pin 0 being -64, N=5 with esp8266 and N=8 with esp32)
      pwmN(val) = outputs a pwm signal on channel N (1..N) with val (0-1023)
      wifis = Wi-Fi connection status: 0 = disconnected, >0 = connected

      wcs = send this line to webpage (WebContentSend)
      wcf = flushes the web buffer (WSContentFlush)
      wfs = send this file to webpage
      rapp = append this line to MQTT (ResponseAppend)
      wm = contains source of web request code e.g. 0 = Sensor display (FUNC_WEB_SENSOR)

      acp(dst src) = copy array

      knx(code value) = sends a number value to KNX

      sml(m 0 bd) = set SML baud rate of Meter m to bd (baud)
      sml(m 1 htxt) = send SML Hex string htxt as binary to Meter m
      sml(-m 1 initstr) = reinits serial port of Meter m, initstr: "baud:mode" e.g. "9600:8E1", currently only baud and N,E,O are evaluated.
      sml(m 2) = reads serial data received by Meter m into string (if m<0 reads hex values, else asci values) sml(m 3 hstr) = inserts SML Hexstring variable hstr as binary to Meter m in Output stream e.g. for special MODBUS cmds, hstr must be a string variable NO string constant
      sml[n] = get value of SML energy register n
      smls[m] = get value of SML meter string info of meter m, if m < 0 gets string representation of numeric value of decode line m, this enables double number resolution.
      smlv[n] = get SML decode valid status of line n (1..N), returns 1 if line decoded. n=0 resets all status codes to zero smld(m) = call decoder of meter m
      smlj = read or write variable, when 0 disables MQTT output of SML.
      enrg[n] = get value of energy register n 0=total, 1..3 voltage of phase 1..3, 4..6 current of phase 1..3, 7..9 power of phase 1..3, 10=start energy, 11=daily energy, 12=energy yesterday (if defined USE_ENERGY_SENSOR)
      gjp("host" "path") = trigger HTTPS JSON page read as used by Tesla Powerwall (if defined SCRIPT_GET_HTTPS_JP)
      gwr("del" index (ec)) = gets non JSON element from webresponse del = delimiter char or string, index = n´th element, optional end character delimiter ec. (if defined USE_WEBSEND_RESPONSE)
      http("url" "payload") = does a GET or POST request on a URL (http:// is internally added) tsN(ms) = set up to 4 timers (N=1..4) to millisecond time on expiration triggers section >tiN (if defined USE_SCRIPT_TIMER)
      hours = hours
      mins = mins
      secs = seconds
      day = day of month
      wday = day of week (Sunday=1,Monday=2;Tuesday=3;Wednesday=4,Thursday=5,Friday=6,Saturday=7)
      month = month
      year = year
      epoch = epoch time (from 2019-1-1 00:00:00)
      epoffs = set epoch offset, (must be no longer then 2 years to fit into single float with second precision)
      eres = result of >E section set this var to 1 in section >E to tell Tasmota event is handled (prevents MQTT)

      The following variables are cleared after reading true:
      chg[var] = true if a variables value was changed (numeric vars only) diff[var] = difference since last variable update upd[var] = true if a variable was updated
      boot = true on BOOT
      tinit = true on time init
      tset = true on time set
      mqttc = true on mqtt connect
      mqttd = true on mqtt disconnect
      wific = true on Wi-Fi connect
      wifid = true on Wi-Fi disconnect

      System variables (for debugging)
      stack = stack size
      heap = free heap size
      pheap = PSRAM free heap size (ESP32)
      core = current core (0 or 1) (ESP32)
      ram = used ram size
      slen = script length
      freq = cpu frequency
      micros = running microseconds
      millis = running milliseconds
      loglvl = loglevel of script cmds (may be set also)

      Remarks:
      If you define a variable with the same name as a special variable that special variable is discarded

      Commands~

      => <command> Execute cmd with MQTT output enabled
      -> <command> Execute cmd with MQTT output disabled, recursion disabled. Do not send MQTT or log messages (i.e., silent execute - useful to reduce traffic)
      +> <command> Execute cmd with MQTT output enabled, recursion enabled.

      Warning

      Recursion: If you execute a tasmota cmd in an >E section and this cmd itself executes >E you will get an infinite loop. this is disabled normally and enabled by the +> in case you know what you are doing

      Variable Substitution
      - A single percent sign must be given as %%
      - Variable replacement within commands is allowed using %varname%. Optionally, the decimal places precision for numeric values may be specified by placing a digit (%Nvarname%, N = 0..9) in front of the substitution variable (e.g., Humidity: %3hum%%% will output Humidity: 43.271%)
      - instead of variables arbitrary calculations my be inserted by bracketing %N(formula)%
      - Linefeed, tab and carriage return may be defined by \n, \t and \r

      Special commands:
      print or =>print prints to the log for debugging
      A Tasmota MQTT RESULT message invokes the script's E section. Add print statements to debug a script.

      Example

      >E
      +slider=Dimmer
      +power=POWER
      +
      +if upd[slider]>0
      +then
      +print slider updated %slider%
      +endif
      +
      +if upd[power]>0
      +then
      +print power updated %power%
      +endif
      +

      break exits a section or terminates a for next loop
      dpx sets decimal precision to x (0-9)
      dpx.y sets preceding digits to x and decimal precision to y (0-9)
      dp(x y) sets preceding digits to x and decimal precision to y
      svars save permanent vars
      delay(x) pauses x milliseconds (should be as short as possible)
      beep(f l) (ESP32) beeps with a passive piezo beeper. beep(-f 0) attaches PIN f to the beeper, beep(f l) starts a sound with frequency f (Hz) and len l (ms). f=0 stops the sound.
      spin(x b) set GPIO x (0..16) to value b (0,1). Only bit 0 of b is used - even values set the GPIO to 0 and uneven values set the GPIO to 1
      spinm(x m) set GPIO x (0..16) to mode m (input=0, output=1, input with pullup=2,alternatively b may be: O=out, I=in, P=in with pullup)
      ws2812(array dstoffset) copies an array (defined with m:vname) to the WS2812 LED chain. The array length should be defined as long as the number of pixels. Color is coded as 24 bit RGB. optionally the destination offset in the LED chain may be given
      if dstoffset is flagged by 0x1000, 2 values 16 bits each in an array are used for 32 bit RGBW pixels hsvrgb(h s v) converts hue (0..360), saturation (0..100) and value (0..100) to RGB color
      dt display text command (if #define USE_DISPLAY)

      Subroutines and Parameters #name names a subroutine. Subroutine is called with =#name
      #name(param) names a subroutine with a parameter.
      Each parameter variable must be declared in the '>D' section.
      A subroutine with multiple parameters is declared as '#name(p1 p2 p3)', i.e. spaces between parameters.
      A subroutine is invoked with =#name(param) or '=#name(p1 p2)
      Invoking a subroutine sets the parameter variable to the corresponding expression of the invocation. This means that parameter variables have script wide scope, i.e. they are not local variables to the subroutine.
      Subroutines end with the next # or > line or break. Subroutine invocations may be nested (each level uses about 600 bytes stack space, so nesting level should not exceed 4). Parameters can be numbers or strings and on type mismatch are converted.

      If #define USE_SCRIPT_SUB_COMMAND is included in your user_config_override.h, a subroutine may be invoked via the Console or MQTT using the subroutine's name. For example, a declared subroutine #SETLED(num) may be invoked by typing SETLED 1 in the Console. The parameter 1 is passed into the num argument. This also works with string parameters. since Tasmota capitalizes all commands you must use upper case labels.

      It is possible to "replace" internal Tasmota commands. For example, if a #POWER1(num) subroutine is declared, the command POWER1 is processed in the scripter instead of in the main Tasmota code.

      String parameter should be passed within double quotas: CUSTOMCMD "Some string here"

      =(svar) executes a routine whose name is passed as a string in a variable (dynamic or self modifying code). The string has to start with > or =# for the routine to be executed.

      a subroutine may return a value (number or string):
      return var

      a subroutine is called with: var=#sub(x) when returning a value
      or
      =#sub(x) when not returning a value

      D
      +svar="=#subroutine"
      +
      +S
      +=(svar)
      +
      +#subroutine
      +print subroutine was executed
      +

      For loop (loop count must not be less than 1, no direct nesting supported)

      for var <from> <to> <inc>  
      +next  
      +

      Switch selector (numeric or string)

      switch x  
      +case a  
      +case b  
      +ends  
      +

      Conditional Statements
      There are two syntax alternatives. You may NOT mix both formats.

      if a==b  
      +and x==y  
      +or k==i  
      +then = do this  
      +else = do that  
      +endif  
      +

      or

      if a==b  
      +and x==y  
      +or k==i {  
      +  = do this  
      +} else {  
      +  = do that  
      +}  
      +

      Remarks:
      The last closing bracket must be on a separate line
      Calculations are permitted in conditional expressions, e.g.,

      if var1-var2==var3*var4
      +

      Conditional expressions may be enclosed in parentheses. The statement must be on a single line. e.g.,

      if ((a==b) and ((c==d) or (c==e)) and (s!="x"))
      +

      Mapping Function

      mp(x cond1 result1 cond2 result2 ... cond<n> result<n>)  
      +
      It addresses a standard task with less code and much flexibility: mapping an arbitrary incoming numeric value into the allowed range. The numeric value x (float only - no integer I:) passed as the first parameter is followed by parameter pairs which can be repeated. A parameter pair consists of condition and result. So input value x is compared to the conditions in the order they are provided as subsequent parameters. If the value matches the condition, the associated result is returned as function. Subsequent rules are skipped. If x matches none of the conditions, x is returned unchanged as result.
      Conditions consist of one of the comparison operators "<", ">", "=" followed by a numeric value/variable. Be noted that 2-char-operators like ">=" are not allowed. Results consist of a numeric value/variable.

      Example 1: y=mp(x <8 0)
      +           This mapping reads: If x is less than 8 return 0, otherwise return x
      +                                                          .
      +Example 2: y=mp(x >100 100)
      +           This mapping reads: If x is greater than 100 return 100, otherwise x.
      +
      +Example 3: y=mp(x <8 0 >100 100)
      +           This mapping reads: Assigns 0 to y if x is less than 8. Assigns 100 to y if x is greater than 100. 
      +                               Assigns x to y for all values of x that do not meet the above criteria (8 to 100).
      +The above code of example 3 does the same as the following code - with just one line of code and 16 characters less:
      +y=x
      +if x<8 {
      +y=0
      +}
      +if x>100 {
      +y=100
      +}
      +

      E-mail
      #define USE_SENDMAIL
      Enabling this feature also enables Tasmota TLS as sendmail uses SSL.

      sendmail [server:port:user:passwd:from:to:subject] msg

      Example

      sendmail [smtp.gmail.com:465:user:passwd:<sender@gmail.com>:<rec@gmail.com>:alarm] %string%
      +

      Remark:
      A number of e-mail servers (such as Gmail) require the receiver's e-mail address to be enclosed by angle brackets < ... > as in example above. Most other e-mail servers also accept this format. While ESP8266 sendmail needs brackets, ESP32 sendmail inserts brackets itself so you should not specify brackets here.

      Warning

      Don't use your Google account password with GMAIL SMTP server.
      You must create an Application specific password

      The following parameters can be specified during compilation via #define directives in user_config_override.h:
      * EMAIL_SERVER
      * EMAIL_PORT
      * EMAIL_USER
      * EMAIL_PASSWORD
      * EMAIL_FROM

      To use any of these values, pass an * as its corresponding argument placeholder.

      Example

      sendmail [*:*:*:*:*:<rec@gmail.com>:theSubject] theMessage

      Instead of passing the msg as a string constant, the body of the e-mail message may also be composed using the script m (note lower case) section. The specified text in this script section must end with a # character. sendmail will use the m section if * is passed as the msg parameter. in this >m section you may also specify email attachments. @/filename specifies a file to be attached (if file system is present)
      &arrayname specifies an array attachment (as tab delimited text, no file system needed)
      $N attach a webcam picture from rambuffer number N (usually 1)

      See [Scripting Cookbook Example].(#send-e-mail)

      Subscribe, Unsubscribe
      #define SUPPORT_MQTT_EVENT
      subscribe and unsubscribe commands are supported. In contrast to rules, no event is generated but the event name specifies a variable defined in D section and this variable is automatically set on transmission of the subscribed item
      within a script the subscribe cmd must be send with +> instead of =>
      the MQTT decoder may be configured for more space in user config overwrite by
      #define MQTT_EVENT_MSIZE xxx (default is 256)
      #define MQTT_EVENT_JSIZE xxx (default is 400)

      File System Support
      #define USE_UFILESYS
      optional for SD_CARD:
      #define USE_SDCARD
      #define SDCARD_CS_PIN X X = GPIO of card chip select
      SD card uses standard hardware SPI GPIO: mosi,miso,sclk
      depending on used linker file you get a flash file system with the same functionality but very low capacity (e.g. 2 MB)
      A maximum of four files may be open at a time
      e.g., allows for logging sensors to a tab delimited file and then downloading the file (see Sensor Logging example)
      The script itself is also stored on the file system with a default size of 8192 characters

      fr=fo("fname" m) open file fname, mode 0=read, 1=write, 2=append (returns file reference (0-3) or -1 for error (alternatively m may be: r=read, w=write, a=append). For files on SD card, filename must be preceded with / e.g. fr=fo("/fname.txt" 0) res=fw("text" fr) writes text to (the end of) file fr, returns number of bytes written
      res=fr(svar fr) reads a string into svar, returns bytes read. String is read until delimiter (\t \n \r) or eof
      fc(fr) close file
      ff(fr) flush file, writes cached data and updates directory
      fd("fname") delete file fname
      frn("spath" "dpath") rename a file
      flx(fname) create download link for file (x=1 or 2) fname = file name of file to download
      fsm return 1 if filesystem is mounted, (valid SD card found)
      res=fsi(sel) gets file system information, sel=0 returns total media size, sel=1 returns free space both in kB
      fra(array fr) reads array from open file with fr (assumes tab delimited entries)
      fwa(array fr) writes array to open file with fr (writes tab delimited entries)
      fz(fr) returns file size
      fa(fr) returns number of available bytes in open file stream
      fs(fr pos) seek to file position pos
      fwb(byte fr) write byte to file
      frb(fr) read byte from file
      frw(fr url) read file from web url

      Other commands (+?? flash)
      #define USE_FEXTRACT
      fxt(fr ts_from ts_to col_offs accum array1 array2 ... arrayn) read arrays from csv file from timestamp to timestamp with column offset and accumulate values into arrays1 .. N, assumes csv file with timestamp in 1. column and data values in columns 2 to n.
      fxto(... same as above with time optimized access
      cts(tstamp flg) convert timestamp to German locale format back and forth flg=0 to webformat, 1 to German format
      tso(tstamp day flag) add time offset in days to timestamp optional flg = char 0 zo zero time HH:MM:SS
      tsn(tstamp) convert timestamp to seconds
      s2t(seconds) convert seconds to Tasmota timestamp

      Extended commands (+0,9k flash)
      #define USE_SCRIPT_FATFS_EXT
      fmd("fname") make directory fname
      frd("fname") remove directory fname
      fx("fname") check if file fname exists
      fe("fname") execute script fname (max 2048 bytes, script must start with the '>' character on the first line)
      lfw("fname" payload limit) logs a string (payload) to a file (fname) with size limit (limit) paylyoad is added to end of file together with a LF character. if file size is exceeded first line of file is removed.

      ESP32 real Multitasking support
      #define USE_SCRIPT_TASK enables support for multitasking scripts
      res=ct(num timer core (prio) (stack))
      creates a task num (1 or 2) with optional priority and stack size
      which is executed every timer (ms) time
      on core 0 or 1

      the sections are named
      >t1 for task 1
      >t2 for task 2

      Example

      >D
      +>B
      +; create task 1 every 1000 ms on core 0
      +ct(1 1000 0)
      +; create task 2 every 3000 ms on core 1
      +ct(2 3000 1)
      +
      +>t1
      +print task1 on core %core%
      +
      +>t2
      +print task2 on core %core%
      +
      minimal LVGL support
      #define USE_LVGL
      to test LVGL a few functions are implemented:
      lvgl(sel ...) general lvgl call
      each object gets a concurrent number 1 ... N with which you can reference the object sel = 0 => initialize LVGL with current display sel = 1 => clear screen
      sel = 2 xp yp xs ys text => create a button. the button press is reported in section >lvb
      sel = 3 xp yp xs ys => create a slider. the slider move is reported in section >lvs
      sel = 4 xp yp xs ys min max => create a gauge.
      set = 5 objnr value => set gauge value.
      sel = 6 xp yp xs ys text => create a label.
      sel = 7 objnr text => set label text
      sel = 8 create a keyboard, just get a look and feel

      sel = 50 => get obj nr from caller in callback >lvb or >lvs
      sel = 51 => get event nr from caller in callback >lvb or >lvs
      sel = 52 => get slider value from caller in callback >lvs

      minimal LVGL support
      #define USE_LVGL
      to test LVGL a few functions are implemented:
      lvgl(sel ...) general lvgl call
      each object gets a concurrent number 1 ... N with which you can reference the object sel = 0 => initialize LVGL with current display sel = 1 => clear screen
      sel = 2 xp yp xs ys text => create a button. the button press is reported in section >lvb
      sel = 3 xp yp xs ys => create a slider. the slider move is reported in section >lvs
      sel = 4 xp yp xs ys min max => create a gauge.
      set = 5 objnr value => set gauge value.
      sel = 6 xp yp xs ys text => create a label.
      sel = 7 objnr text => set label text
      sel = 8 create a keyboard, just get a look and feel

      sel = 50 => get obj nr from caller in callback >lvb or >lvs
      sel = 51 => get event nr from caller in callback >lvb or >lvs
      sel = 52 => get slider value from caller in callback >lvs

      ESP32 Webcam support
      #define USE_WEBCAM
      Template for AI THINKER CAM :

      remarks:
      - GPIO0 zero must be disconnected from any wire after programming because this pin drives the cam clock and does not tolerate any capacitive load
      - Only boards with PSRAM should be used. To enable PSRAM board should be se set to esp32cam in common32 of platform_override.ini
      board = esp32cam
      - To speed up cam processing CPU frequency should be better set to 240Mhz in common32 of platform_override.ini
      board_build.f_cpu = 240000000L

      file system extension:
      fwp(pnum fr) write picture from RAM buffer number pnum to SD card file with file reference fr
      specific webcam commands:
      res=wc(sel p1 p2) control webcam, sel = function selector p1 ... optional parameters
      res=wc(0 pres) init webcam with picture resolution pres, returns 0 when error, 2 when PSRAM found, else 1
      pres
      * 0 = FRAMESIZE_QQVGA, // 160x120
      * 1 = FRAMESIZE_QQVGA2, // 128x160
      * 2 = FRAMESIZE_QCIF, // 176x144
      * 3 = FRAMESIZE_HQVGA, // 240x176
      * 4 = FRAMESIZE_QVGA, // 320x240
      * 5 = FRAMESIZE_CIF, // 400x296
      * 6 = FRAMESIZE_VGA, // 640x480
      * 7 = FRAMESIZE_SVGA, // 800x600
      * 8 = FRAMESIZE_XGA, // 1024x768
      * 9 = FRAMESIZE_SXGA, // 1280x1024
      * 10 = FRAMESIZE_UXGA, // 1600x1200

      res=wc(1 bnum) capture picture to rambuffer bnum (1..4), returns framesize of picture or 0 when error
      res=wc(2 sel p1) execute various controls, details below.
      res=wc(3) gets picture width
      res=wc(4) gets picture height
      res=wc(5 p) start stop streaming 0=stop, 1=start
      res=wc(6 p) start stop motion detector, p=0 => stop detector, p=T start detector with picture every T ms, -1 get picture difference, -2 get picture brightness
      res=wc(7 p) start stop face detector, p=0 => stop detector, p=T start detector with picture every T ms, -1 get number of faces found in picture (USE_FACE_DETECT must be defined)

      control cmds sel =
      * 0 fs = set frame size (see above for constants)
      * 1 se = set special effect

      • 0 = no effect
      • 1 = negative
      • 2 = black and white
      • 3 = reddish
      • 4 = greenish
      • 5 = blue
      • 6 = retro

      • 2 fl = set horizontal flip 0,1

      • 3 mi = set vertical mirror 0,1

      to read a value without setting pass -1

      • extensions to the email system on ESP32
        #define SEND_EMAIL and #define USE_ESP32MAIL
        enables specific ESP32 mail server
        this server can handle more mail servers by supporting START_TLS
        remark: mail addresses must not be enclosed with <> because the server inserts them automatically
        this server also supports email attachments
        in the >m section you may write
        &/file.txt to attach a file from SD card
        $N N=1..4 to attach a picture from picture RAM buffer number N

      • displaying webcam pictures in WEBUI
        you may display a webcam picture by giving the name /wc.jpg?p=N (1..4) for RAM picturebuffer N
        "<img src="/wc.jpg?p=1" alt="webcam image" >"
        you may also provide the picture size (h and v have to be preset before)
        "<img src="/wc.jpg?p=1" alt="webcam image" style="width:%w%px;height:%h%px;">"
        if you precede the line by & char the image is displayed in the main section, else in the sensor tab section

      the webcam stream can be specified by the following line
      lip is a system variable containing the local device ip
      "&<br>"
      "&<img src="http://%lip%:81/stream" style="width:%w%px;height:%h%px">"
      "&<br><center>webcam stream"

      remark: the Flash illumination LED is connected to GPIO4

      Example

          >D
      +    res=0
      +    w=0
      +    h=0
      +    mot=0
      +    bri=0
      +
      +    >B
      +    ; init cam with QVGA
      +    res=wc(0 4)
      +    ; get pixel size
      +    w=wc(3)
      +    h=wc(4)
      +    ; start motion detector, picture every 1000 ms
      +    mot=wc(6 1000)
      +
      +    >S
      +    if wific>0
      +    then
      +    ; when wifi up, start stream
      +    res=wc(5 1)
      +    endif
      +
      +    ; get motion detect diff value
      +    mot=wc(6 -1)
      +    ; get picture brightnes
      +    bri=wc(6 -2)
      +
      +    >W
      +    <center>motion diff = %mot%<br>
      +    <center>brightness = %bri%<br>
      +    ; show stream on WEBUI
      +    &<br>
      +    &<img src="http://%lip%:81/stream" style="width:%w%px;height:%h%px">
      +    &<br><center>webcam stream
      +

      Scripting Cookbook~

      a valid script must start with >D in the first line!  
      +some samples still contain comment lines before >D. This is no longer valid!
      +

      simple example to start with~

      >D
      +; in this section you may define and or preset variables, there are numbers or strings.
      +; in contrast to rules you may choose any variable name
      +; numeric variable
      +val1=1.234
      +; numeric variable that is preserved after reboot or power down
      +p:val2=0
      +; text variable
      +txt="hello world"
      +
      +>B
      +; this section is executed durig boot or on script restart
      +print we are booting
      +
      +>S
      +; this section is executed every second
      +print one second tick
      +; variables may be printed enclosed with % char, thus showing "hello world 1.234"
      +; very handy for debugging
      +print %txt% %val1%
      +
      +; check if upcounting seconds give zero result when dividing by 10
      +; upsecs is a system defined variable that counts seconds from start
      +
      +; you may use if then, else, endif
      +if upsecs%10==0
      +then
      +    print every 10 seconds
      +endif
      +
      +; or if {} else {}
      +if upsecs%10==0 {
      +    print every 10 seconds
      +}
      +
      +>R
      +; this section is executed on restart
      +print we are restarting
      +

      Scripting Language Example~

      **Actually this code is too large**. This is only meant to show some of the possibilities
      +
      +>D
      +; define all vars here
      +p:mintmp=10  (p:means permanent)
      +p:maxtmp=30
      +t:timer1=30  (t:means countdown timer)
      +t:mt=0
      +i:count=0  (i:means auto counter)
      +hello="hello world"
      +string="xxx"
      +url="[_IP_]";
      +hum=0
      +temp=0
      +zigbeetemp=0
      +timer=0
      +dimmer=0
      +sw=0
      +rssi=0
      +param=0
      +
      +col=""
      +ocol=""
      +chan1=0
      +chan2=0
      +chan3=0
      +
      +ahum=0
      +atemp=0
      +tcnt=0
      +hour=0
      +state=1
      +m:med5=0
      +M:movav=0
      +; define array with 10 entries
      +m:array=0 10
      +
      +>B
      +string=hello+"how are you?"
      +print BOOT executed
      +print %hello%
      +=>mp3track 1
      +
      +; list gpio pin definitions
      +for cnt 0 16 1
      +tmp=pd[cnt]
      +print %cnt% = %tmp%
      +next
      +
      +; get gpio pin for relais 1
      +tmp=pn[21]
      +print relais 1 is on pin %tmp%
      +
      +; pulse relais over raw gpio
      +spin(tmp 1)
      +delay(100)
      +spin(tmp 0)
      +
      +; raw pin level
      +print level of gpio1 %pin[1]%
      +
      +; pulse over tasmota cmd
      +=>power 1
      +delay(100)
      +=power 0
      +
      +>T
      +hum=BME280#Humidity
      +temp=BME280#Temperature
      +rssi=Wifi#RSSI
      +string=SleepMode
      +
      +; add to median filter
      +median=temp
      +; add to moving average filter
      +movav=hum
      +
      +; show filtered results
      +print %median% %movav%
      +
      +if chg[rssi]>0
      +then print rssi changed to %rssi%
      +endif
      +
      +if temp>30
      +and hum>70
      +then print damn hot!
      +endif
      +
      +=#siren(5)
      +
      +; loop nesting workaround
      +; by using subroutine
      +#siren(num)
      +for cnt 1 num 1
      +=#stone
      +next
      +
      +#stone
      +for tone 2000 1000 -20
      +beep(tone 10);
      +delay(12)
      +next
      +
      +>S
      +; every second but not completely reliable time here
      +; use upsecs and uptime or best t: for reliable timers
      +
      +; arrays
      +array[1]=4
      +array[2]=5
      +tmp=array[1]+array[2]
      +
      +; call subrountines with parameters
      +=#sub1("hallo")
      +=#sub2(999)
      +
      +; stop timer after expired
      +if timer1==0
      +then timer1=-1
      +print timer1 expired
      +endif
      +
      +; auto counter with restart
      +if count=10
      +then print 10 seconds over
      +count=0
      +endif
      +
      +if upsecs%5==0
      +then print %upsecs%  (every 5 seconds)
      +endif
      +
      +; not recommended for reliable timers
      +timer+=1
      +if timer>=5
      +then print 5 seconds over (may be)
      +timer=0
      +endif
      +
      +dimmer+=1
      +if dimmer>100
      +then dimmer=0
      +endif
      +
      +=>dimmer %dimmer%
      +=>WebSend %url% dimmer %dimmer%
      +
      +; show on display
      +dp0
      +dt [c1l1f1s2p20] dimmer=%dimmer%
      +
      +print %upsecs% %uptime% %time% %sunrise% %sunset% %tstamp%
      +
      +if time>sunset
      +or time<sunrise
      +then
      +; night time
      +if pwr[1]==0
      +then =>power1 1
      +endif
      +else
      +; day time
      +if pwr[1]>0
      +then =>power1 0
      +endif
      +endif
      +
      +; clr display on boot
      +if boot>0
      +then dt [z]
      +endif
      +
      +; frost warning
      +if ((temp<0 or zigbeetemp<0) and mt<=0)
      +then =#sendmail("frost alert")
      +; alarm only every 5 minutes
      +mt=300
      +=mp3track 2
      +endif
      +
      +; var has been updated
      +if upd[hello]>0
      +then print %hello%
      +endif
      +
      +; send to Thingspeak every 60 seconds
      +; average data in between
      +if upsecs%60==0
      +then
      +ahum>=tcnt
      +atemp>=tcnt
      +=WebSend [_IP_]/update?key=_token_&field1=%atemp%&field2=%ahum%
      +tcnt=0
      +atemp=0
      +ahum=0
      +else
      +ahum+=hum
      +atemp+=temp
      +tcnt+=1
      +endif
      +
      +hour=int(time/60)
      +if chg[hour]>0
      +then
      +; exactly every hour
      +print full hour reached
      +endif
      +
      +if time5 {
      +print more then 5 minutes after midnight
      +} else {
      +print less then 5 minutes after midnight
      +}
      +
      +; publish abs hum every teleperiod time
      +if mqtts>0
      +and upsecs%tper==0
      +then
      +; calc abs humidity
      +tmp=pow(2.718281828 (17.67*temp)/(temp+243.5))
      +tmp=(6.112*tmp*hum*18.01534)/((273.15+temp)*8.31447215)
      +; publish median filtered value
      +=>Publish tele/%topic%/SENSOR {"Script":{"abshum":%med(0 tmp)%}}
      +endif
      +
      +;switch case state machine
      +switch state
      +case 1
      +print state=%state% , start
      +state+=1
      +case 2
      +print state=%state%
      +state+=1
      +case 3
      +print state=%state%  , reset
      +state=1
      +ends
      +
      +; subroutines
      +#sub1(string)
      +print sub1: %string%
      +#sub2(param)
      +print sub2: %param%
      +
      +#sendmail(string)
      +=>sendmail [smtp.gmail.com:465:user:passwd:<sender@gmail.de:<rec@gmail.de:alarm] %string%
      +
      +>E
      +print event executed!
      +
      +; Assign temperature from a Zigbee sensor
      +zigbeetemp=ZbReceived#0x2342#Temperature
      +; get HSBColor 1. component
      +tmp=st(HSBColor , 1)
      +
      +; check if switch changed state
      +sw=sw[1]
      +if chg[sw]>0
      +then =>power1 %sw%
      +endif
      +
      +hello="event occurred"
      +
      +; check for Color change (Color is a string)
      +col=Color
      +; color change needs 2 string vars
      +if col!=ocol
      +then ocol=col
      +print color changed  %col%
      +endif
      +
      +; or check change of color channels
      +chan1=Channel[1]
      +chan2=Channel[2]
      +chan3=Channel[3]
      +
      +if chg[chan1]>0
      +or chg[chan2]>0
      +or chg[chan3]>0
      +then = color has changed
      +endif
      +
      +; compose color string for red
      +col=hn(255)+hn(0)+hn(0)
      +=color %col%
      +
      +>R
      +print restarting now
      +

      Sensor Logging~

      ; define all vars here
      +; reserve large strings
      +>D 48
      +hum=0
      +temp=0
      +fr=0
      +res=0
      +cnt=0
      +; moving average for 60 seconds
      +M:mhum=0 60
      +M:mtemp=0 60
      +str=""
      +
      +>B
      +; set sensor file download link
      +;fl1("slog.txt")
      +; delete file in case we want to start fresh
      +;fd("slog.txt")
      +
      +; list all files in root directory
      +fr=fo("/" 0)
      +for cnt 1 20 1
      +res=fr(str fr)
      +if res>0
      +then
      +print %cnt% : %str%
      +else
      +break
      +endif
      +next
      +fc(fr)
      +
      +>T
      +; get sensor values
      +temp=BME280#Temperature
      +hum=BME280#Humidity
      +
      +>S
      +; average sensor values every second
      +mhum=hum
      +mtemp=temp
      +
      +; write average to sensor log every minute
      +if upsecs%60==0
      +then
      +; open file for write
      +fr=fo("slog.txt" 1)
      +; compose string for tab delimited file entry
      +str=s(upsecs)+"\t"+s(mhum)+"\t"+s(mtemp)+"\n"
      +; write string to log file
      +res=fw(str fr)
      +; close file
      +fc(fr)
      +endif
      +
      +>R
      +

      global variables example~

      make temperature and humidity of an SHT sensor public
      all devices in the local network may use the global variables needs #define USE_SCRIPT_GLOBVARS

      Sender:

      >D
      +g:temp=0
      +g:hum=0
      +
      +>T
      +temp=SHT3X_0x44#Temperature
      +hum=SHT3X_0x44#Humidity
      +

      Receiver(s) displays the value on a display

      >D
      +g:temp=0
      +g:hum=0
      +
      +>S
      +dt [l1c1p10]temp=%temp% C
      +dt [l2c1p10]hum=%hum% %%
      +

      e-Paper 29 Display with SGP30 and BME280~

      Some variables are set from ioBroker

      >D
      +hum=0
      +temp=0
      +press=0
      +ahum=0
      +tvoc=0
      +eco2=0
      +zwz=0
      +wr1=0
      +wr2=0
      +wr3=0
      +otmp=0
      +pwl=0
      +tmp=0
      +; preset units in case they are not available
      +punit="hPa"
      +tunit="C"
      +
      +>B
      +;reset auto draw
      +dt [zD0]
      +;clr display and draw a frame
      +dt [x0y20h296x0y40h296]
      +
      +>T
      +; get telemetry sensor values
      +temp=BME280#Temperature
      +hum=BME280#Humidity
      +press=BME280#Pressure
      +tvoc=SGP30#TVOC
      +eco2=SGP30#eCO2
      +ahum=SGP30#aHumidity
      +tunit=TempUnit
      +punit=PressureUnit
      +
      +>S
      +; update display every [`TelePeriod`](Commands.md#teleperiod)
      +if upsecs%tper==0
      +then
      +dp2
      +dt [f1p7x0y5]%temp% %tunit%
      +dt [p5x70y5]%hum% %%[x250y5t]
      +dt [p11x140y5]%press% %punit%
      +dt [p10x30y25]TVOC: %tvoc% ppb
      +dt [p10x160y25]eCO2: %eco2% ppm
      +dt [p10c26l5]ahum: %ahum% g^m3
      +
      +dp0
      +dt [p25c1l5]WR 1 (Dach)  : %wr1% W
      +dt [p25c1l6]WR 2 (Garage): %-wr3% W
      +dt [p25c1l7]WR 3 (Garten): %-wr2% W
      +dt [p25c1l8]Aussentemperatur: %otmp% C
      +dt [x170y95r120:30f2p6x185y100] %pwl% %%
      +; now update screen
      +dt [d]
      +endif
      +
      +>E
      +
      +>R
      +

      e-Paper 42 Display with SHT31 and BME280~

      This script shows 2 graphs on a 4.2 inch e-Paper display: 1. some local sensors, and 2. power statistics

      • The first graph is the battery level of a solar battery (Tesla PowerWall 2)
      • The second graph shows the solar yield of the roof panels in Watts
      • Another special feature is that this script displays daily and weekly averages (via moving average) of all power IO of the house.
      • it sends an email every Sunday night with the weekly data
      • it displays a google bar chart on the webui with values for each weekday of the last week
      • ESP32 CPU with SD card
      • Since the display is a full update panel it is updated only once a minute
      • Some values (like power meters) are set remotely from ioBroker

      >D
      +hum=0
      +temp=0
      +press=0
      +zwz=0
      +wr1=0
      +wr2=0
      +wr3=0
      +otmp=0
      +pwl=0
      +ez1=0
      +sez1=0
      +M:mez1=0 7
      +ezh=0
      +sezh=0
      +M:mezh=0 7
      +vzh=0
      +svzh=0
      +M:mvzh=0 7
      +wd=0
      +res=0    
      +hr=0
      +t1=0
      +res=0
      +
      +>B
      +->setoption64 1
      +tper=30
      +
      +dt [IzD0]
      +dt [zG10352:5:40:-350:80:10080:0:100f3x360y40]100 %%[x360y115]0 %%
      +dt [f1x100y25]Powerwall - 7 Tage[f1x360y75] 0 %%
      +dt [G10353:5:140:-350:80:10080:0:5000f3x360y140]+5000 W[x360y215]0 W
      +dt [f1x70y125]Volleinspeisung - 7 Tage[f1x360y180] 0 W
      +dt [p13x10y230]WR 1,2,3:
      +dt [p13x10y245]H-Einsp.:
      +dt [p13x10y260]H-Verbr.:
      +dt [p13x10y275]D-Einsp.:
      +dt [d]
      +
      +dt [Gr0:/g0_sav.txt:]
      +dt [Gr1:/g1_sav.txt:]
      +
      +beep(-25 0)
      +beep(1000 100)
      +
      +>T
      +press=BMP280#Pressure
      +temp=SHT3X_0x44#Temperature
      +hum=SHT3X_0x44#Humidity
      +
      +>S
      +
      +if upsecs%60==0
      +then
      +dp2
      +dt [f1p7x0y5]%temp% C
      +dt [x0y20h400x250y5T][x350t][f1p10x70y5]%hum% %%
      +dt [p10x140y5]%press% hPa
      +dp0
      +dt [p5x360y75]%pwl% %%
      +dt [p6x360y180]%wr1%W
      +dt [g0:%pwl%g1:%wr1%]
      +
      +dt [p24x75y230] %wr1% W : %-wr2% W : %-wr3% W
      +dt [p-10x75y245]%ezh% kWh
      +dt [p-10x75y260]%vzh% kWh
      +dt [p-10x75y275]%ez1% kWh
      +
      +t1=mezh*7
      +dt [p-10x150y245]: %t1% kWh
      +t1=mvzh*7
      +dt [p-10x150y260]: %t1% kWh
      +t1=mez1*7
      +dt [p-10x150y275]: %t1% kWh
      +
      +dp1
      +t1=ezh-sezh
      +dt [p12x250y245]: %t1% kWh
      +t1=vzh-svzh
      +dt [p12x250y260]: %t1% kWh
      +t1=ez1-sez1
      +dt [p12x250y275]: %t1% kWh
      +
      +dp0
      +dt [f2p5x320y250] %otmp%C
      +
      +dt [d]
      +print updating display
      +endif
      +
      +hr=hours
      +if chg[hr]>0
      +and hr==0
      +then
      +mez1=ez1-sez1
      +sez1=ez1
      +mezh=ezh-sezh
      +sezh=ezh
      +mvzh=vzh-svzh
      +svzh=vzh
      +endif
      +
      +if sezh==0
      +then
      +sez1=ez1
      +sezh=ezh
      +svzh=vzh
      +endif
      +
      +wd=wday
      +if chg[wd]>0
      +and wd==1
      +then
      +=>sendmail [*:*:*:*:*:user.tasmota@gmail.com: Wochenbericht]*
      +print sending email
      +endif
      +
      +
      +if upsecs%300==0
      +then
      +=#savgraf
      +print saving graph
      +endif
      +
      +#savgraf
      +dt [Gs0:/g0_sav.txt:]
      +dt [Gs1:/g1_sav.txt:]
      +
      +>m
      +Wochenbericht Einspeisung und Verbrauch<br><br>
      +w1=%mez1[1]%,%mez1[2]%,%mez1[3]%,%mez1[4]%,%mez1[5]%,%mez1[6]%,%mez1[7]%,%mez1[8]%<br>
      +w2=%mezh[1]%,%mezh[2]%,%mezh[3]%,%mezh[4]%,%mezh[5]%,%mezh[6]%,%mezh[7]%,%mezh[8]%<br>
      +w3=%mvzh[1]%,%mvzh[2]%,%mvzh[3]%,%mvzh[4]%,%mvzh[5]%,%mvzh[6]%,%mvzh[7]%,%mvzh[8]%<br>
      +#
      +>W
      +&<br><div id="container"style="width:640px;height:480px;margin:0 auto"></div><br>
      +&<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
      +&<script type="text/javascript">google.charts.load('current',{packages:['corechart']});</script>
      +&<script language="JavaScript">function drawChart(){var data=
      +&google.visualization.arrayToDataTable([
      +&['weekday','Power'],['Mo',%mvzh[1]%],['Di',%mvzh[2]%],['Mi',%mvzh[3]%],['Do',%mvzh[4]%],
      +&['Fr',%mvzh[5]%],['Sa',%mvzh[6]%],['So',%mvzh[7]%]]);
      +&var options={title:'daily solar feed',isStacked:true};
      +&var chart=new 
      +&google.visualization.ColumnChart(document.getElementById('container'));chart.draw(data,options);}
      +&google.charts.setOnLoadCallback(drawChart);</script>
      +#
      +

      ILI 9488 Color LCD Display with BMP280 and VL5310X~

      Shows various BMP280 energy graphs Turn display on and off using VL5310X proximity sensor to prevent burn-in

      Some variables are set from ioBroker

      >D
      +temp=0
      +press=0
      +zwz=0
      +wr1=0
      +wr2=0
      +wr3=0
      +otmp=0
      +pwl=0
      +tmp=0
      +dist=0
      +punit="hPa"
      +tunit="C"
      +hour=0
      +
      +>B
      +dt [z]
      +
      +// define 2 graphs, 2. has 3 tracks
      +dt [zCi1G2656:5:20:400:80:1440:-5000:5000:3Ci7f3x410y20]+5000 W[x410y95]-5000 W [Ci7f1x70y3] Zweirichtungsz~80hler - 24 Stunden
      +dt  [Ci1G2657:5:120:400:80:1440:0:5000:3Ci7f3x410y120]+5000 W[x410y195]0 W [Ci7f1x70y103] Wechselrichter 1-3 - 24 Stunden
      +dt [Ci1G2658:5:120:400:80:1440:0:5000:16][Ci1G2659:5:120:400:80:1440:0:5000:5]
      +dt [f1s1b0:260:260:100&#8203;:50:2:11:4:2:Rel 1:b1:370:260:100&#8203;:50:2:11:4:2:Dsp off:]
      +=>mp3volume 100
      +=>mp3track 4
      +
      +>T
      +; get some telemetry values
      +temp=BMP280#Temperature
      +press=BMP280#Pressure
      +tunit=TempUnit
      +punit=PressureUnit
      +dist=VL53L0X#Distance
      +
      +; check proximity sensor to turn display on and off to prevent burn-in
      +if dist>300
      +then
      +if pwr[2]>0
      +then
      +=>power2 0
      +endif
      +else
      +if pwr[2]==0
      +then
      +=>power2 1
      +endif
      +endif
      +
      +>S
      +; update graph every teleperiod
      +if upsecs%tper==0
      +then
      +dp2
      +dt [f1Ci3x40y260w30Ci1]
      +dt [Ci7x120y220t]
      +dt [Ci7x180y220T]
      +dt [Ci7p8x120y240]%temp% %tunit%
      +dt [Ci7x120y260]%press% %punit%
      +dt [Ci7x120y280]%dist% mm
      +dp0
      +dt [g0:%zwz%g1:%wr1%g2:%-wr2%g3:%-wr3%]
      +if zwz0
      +then
      +dt [p-8x410y55Ci2Bi0]%zwz% W
      +else
      +dt [p-8x410y55Ci3Bi0]%zwz% W
      +endif
      +dt [p-8x410y140Ci3Bi0]%wr1% W
      +dt [p-8x410y155Ci16Bi0]%-wr2% W
      +dt [p-8x410y170Ci5Bi0]%-wr3% W
      +endif
      +
      +; chime every full hour
      +hour=int(time/60)
      +if chg[hour]>0
      +then ->mp3track 4
      +endif
      +
      +>E
      +
      +>R
      +

      LED Bar Display with WS2812 LED Chain~

      Used to display home's solar power input/output (+-5000 Watts)

      >D
      +m:array=0 60 ;defines array for 60 led pixels
      +cnt=0
      +val=0
      +ind=0
      +; rgb values for grid
      +colr1=0x050000
      +colr2=0x050100
      +colg1=0x000300
      +colg2=0x020300
      +ledbar=0
      +blue=64
      +pixels=60
      +steps=10
      +div=0
      +tog=0
      +max=5000
      +min=-5000
      +pos=0
      +
      +>B
      +div=pixels/steps
      +=#prep
      +ws2812(array)
      +
      +; ledbar is set from broker
      +
      +>S
      +if ledbar<min
      +then ledbar=min
      +endif
      +
      +if ledbar>max
      +then ledbar=max
      +endif
      +
      +pos=(ledbar/max)*(pixels/2)
      +if ledbar>0
      +then
      +pos+=(pixels/2)
      +if pospixels-1
      +then pos=pixels
      +endif
      +else
      +pos+=(pixels/2)+1
      +if pospixels-1
      +then pos=1
      +endif
      +endif
      +
      +if pos<1
      +or pos>pixels
      +then pos=1
      +endif
      +
      +=#prep
      +if ledbar==0
      +then
      +array[pos]=blue
      +array[pos-1]=blue
      +else
      +array[pos]=blue
      +endif
      +
      +; only used if power is off
      +; so lets may be used normally if on
      +if pwr[1]==0
      +then
      +ws2812(array)
      +endif
      +
      +; subroutine for grid
      +#prep
      +for cnt 1 pixels 1
      +ind+=1
      +if ind>div
      +then ind=1
      +tog^=1
      +endif
      +
      +if cnt<=pixels/2
      +then
      +if tog>0
      +then val=colr1
      +else val=colr2
      +endif
      +else
      +if tog>0
      +then val=colg1
      +else val=colg2
      +endif
      +endif
      +array[cnt]=val
      +next
      +
      +>R
      +

      Multiple IR Receiver Synchronization~

      Shows how a Magic Home with IR receiver works Synchronizes 2 Magic Home devices by also sending the commands to a second Magic Home via WebSend

      Script example using if then else ; expand default string length to be able to hold WebSend [xxx.xxx.xxx.xxx]

      >D 25
      +istr=""
      +ws="WebSend [_IP_]"
      +
      +; event section
      +>E
      +; get ir data
      +istr=IrReceived#Data
      +
      +; on
      +if istr=="0x00F7C03F"
      +then
      +->wakeup
      +->%ws% wakeup
      +endif
      +
      +; off
      +if istr=="0x00F740BF"
      +then
      +->power1 0
      +->%ws% power1 0
      +endif
      +
      +;white
      +if istr=="0x00F7E01F"
      +then
      +->color 000000ff
      +->%ws% color 000000ff
      +endif
      +
      +;red
      +if istr=="0x00F720DF"
      +then
      +->color ff000000
      +->%ws% color ff000000
      +endif
      +
      +;green
      +if istr=="0x00F7A05F"
      +then
      +->color 00ff0000
      +->%ws% color 00ff0000
      +endif
      +
      +;blue
      +if istr=="0x00F7609F"
      +then
      +->color 0000ff00
      +->%ws% color 0000ff00
      +endif
      +
      +; dimmer up
      +if istr=="0x00F700FF"
      +then
      +->dimmer +
      +->%ws% dimmer +
      +endif
      +
      +;dimmer down  
      +if istr=="0x00F7807F"  
      +then  
      +->dimmer -  
      +->%ws% dimmer -  
      +endif
      +
      +istr=""
      +

      Script example using switch case ends ; expand default string length to be able to hold WebSend [xxx.xxx.xxx.xxx]

      >D 25
      +istr=""
      +ws="WebSend [_IP_]"
      +
      +; event section
      +>E
      +; get ir data
      +istr=IrReceived#Data
      +
      +switch istr
      +; on
      +case "0x00F7C03F"
      +->wakeup
      +->%ws% wakeup
      +
      +;off
      +case "0x00F740BF"
      +->power1 0
      +->%ws% power1 0
      +
      +;white
      +case "0x00F7E01F"
      +->color 000000ff
      +->%ws% color 000000ff
      +
      +;red
      +case "0x00F720DF"
      +->color ff000000
      +->%ws% color ff000000
      +
      +;green
      +case "0x00F7A05F"
      +->color 00ff0000
      +->%ws% color 00ff0000
      +
      +;blue
      +case "0x00F7609F"
      +->color 0000ff00
      +->%ws% color 0000ff00
      +
      +; dimmer up
      +case "0x00F700FF"
      +->dimmer +
      +->%ws% dimmer +
      +
      +; dimmer down
      +case "0x00F7807F"
      +->dimmer -
      +->%ws% dimmer -
      +ends
      +
      +istr=""
      +

      Fast Polling~

      ; expand default string length to be able to hold `WebSend [xxx.xxx.xxx.xxx]`  
      +>D 25
      +sw=0
      +ws="WebSend [_IP_]"
      +timer=0
      +hold=0
      +toggle=0
      +
      +>B
      +; gpio 5 button input
      +spinm(5,0)
      +
      +; fast section 100ms
      +>F
      +sw=pin[5]
      +; 100 ms timer
      +timer+=1
      +
      +; 3 seconds long press
      +; below 0,5 short press
      +if sw==0
      +and timer5
      +and timer<30
      +then
      +; short press
      +;print short press
      +toggle^=1
      +=>%ws% power1 %toggle%
      +endif
      +
      +if sw>0
      +then
      +;pressed
      +if timer>30
      +then
      +; hold
      +hold=1
      +;print hold=%timer%
      +if toggle>0
      +then
      +=>%ws% dimmer +
      +else
      +=>%ws% dimmer -
      +endif
      +endif
      +else
      +timer=0
      +hold=0
      +endif
      +

      Web UI~

      An example to show how to implement a web UI. This example controls a light via WebSend

      >D
      +dimmer=0
      +sw=0
      +color=""
      +col1=""
      +red=0
      +green=0
      +blue=0
      +ww=0
      +
      +>F
      +color=hn(red)+hn(green)+hn(blue)+hn(ww)
      +if color!=col1
      +then
      +col1=color
      +=>websend [192.168.178.75] color %color%
      +endif
      +
      +if chg[dimmer]>0
      +then  
      +=>websend [192.168.178.75] dimmer %dimmer%
      +endif
      +
      +if chg[sw]>0
      +then
      +=>websend [192.168.178.75] power1 %sw%
      +endif
      +
      +>W
      +bu(sw "Light on" "Light off")
      +ck(sw "Light on/off   ")
      +sl(0 100 dimmer "0" "Dimmer" "100")
      +sl(0 255 red "0" "red" "255")
      +sl(0 255 green "0" "green" "255")
      +sl(0 255 blue "0" "blue" "255")
      +sl(0 255 ww "0" "warm white" "255")
      +tx(color "color:   ")
      +

      Hue Emulation~

      An example to show how to respond to Alexa requests via Hue Emulation

      When Alexa sends on/off, dimmer, and color (via hsb), send commands to a MagicHome device

      >D
      +pwr1=0
      +hue1=0
      +sat1=0
      +bri1=0
      +tmp=0
      +
      +>E
      +if upd[hue1]>0
      +or upd[sat1]>0
      +or upd[bri1]>0
      +then
      +tmp=hue1/182
      +->websend [192.168.178.84] hsbcolor %tmp%,%sat1%,%bri1%
      +endif
      +
      +if upd[pwr1]>0
      +then
      +->websend [192.168.178.84] power1 %pwr1%
      +endif
      +
      +>H
      +; on,hue,sat,bri,ct
      +livingroom,E,on=pwr1,hue=hue1,sat=sat1,bri=bri1
      +

      Alexa Controlled MCP230xx I2C GPIO Expander~

      Uses Tasmota's Hue Emulation capabilities for Alexa interface

      ; define vars
      +>D
      +p:p1=0
      +p:p2=0
      +p:p3=0
      +p:p4=0
      +
      +; init ports
      +>B
      +->sensor29 0,5,0
      +->sensor29 1,5,0
      +->sensor29 2,5,0
      +->sensor29 3,5,0
      +->sensor29 0,%0p1%
      +->sensor29 1,%0p2%
      +->sensor29 2,%0p3%
      +->sensor29 3,%0p4%
      +
      +; define Alexa virtual devices
      +>H
      +port1,S,on=p1
      +port2,S,on=p2
      +port3,S,on=p3
      +port4,S,on=p4
      +
      +; handle events
      +>E
      +print EVENT
      +
      +if upd[p1]>0
      +then
      +->sensor29 0,%0p1%
      +endif
      +if upd[p2]>0
      +then
      +->sensor29 1,%0p2%
      +endif
      +if upd[p3]>0
      +then
      +->sensor29 2,%0p3%
      +endif
      +if upd[p4]>0
      +then
      +->sensor29 3,%0p4%
      +endif
      +
      +=#pub
      +
      +; publish routine
      +#pub
      +=>publish stat/%topic%/RESULT {"MCP23XX":{"p1":%0p1%,"p2":%0p2%,"p3":%0p3%,"p4":%0p4%}}
      +svars
      +
      +; web interface
      +>W
      +bu(p1 "p1 on" "p1 off")bu(p2 "p2 on" "p2 off")bu(p3 "p3 on" "p3 off")bu(p4 "p4 on" "p4 off")
      +

      Retrieve network gateway IP Address~

      >D
      +gw=""
      +
      +; Request Status information. The response will trigger the `U` section
      +>B
      ++>status 5
      +
      +; Read the status JSON payload
      +>U
      +gw=StatusNET#Gateway
      +print %gw%
      +

      Send e-mail~

      >D 25
      +day1=0
      +et=0
      +to="<mrx@gmail.com>"
      +
      +>T
      +et=ENERGY#Total
      +
      +>S
      +; send at midnight
      +day1=day
      +if chg[day1]>0
      +then
      +=>sendmail [*:*:*:*:*:%to%:energy report]*
      +endif
      +
      +>m
      +email report at %tstamp%
      +your power consumption today was %et% KWh
      +#
      +

      Send power reading with formatted time stamp via websend~

      Some web APIs require certain formats (e.g. date & time) to be provided. This example illustrates how to reformat the timestamp and embed it in the get payload. On ESP8266 based devices this is limited to unsecured http (no "s") connections! Don't use this for sensitive data!

      >D 42
      +;long string required for key
      +y=0
      +m=0
      +d=0
      +key="yourkey"
      +id="yourSystemID"
      +ws="WebSend [pvoutput.org]"
      +et=0
      +p=0
      +
      +>T
      +et=ENERGY#Total
      +p=ENERGY#Power
      +; every 5 minutes
      +if upsecs%300==0
      +then
      +y=sb(tstamp 0 4)
      +m=sb(tstamp 5 2)
      +d=sb(tstamp 8 2)
      +=>%ws%/service/r2/addstatus.jsp?key=%key%&sid=%id%&d=%1.0(y)%%2.0(m)%%2.0(d)%&t=%1(sb(tstamp 11 5))%&v2=%s(2.0p)%
      +endif
      +

      Switching and Dimming By Recognizing Mains Power Frequency~

      Switching in Tasmota is usually done by High/Low (+3.3V/GND) changes on a GPIO. However, for devices like the Moes QS-WiFi-D01 Dimmer, this is achieved by a pulse frequency when connected to the GPIO, and these pulses are captured by Counter1 in Tasmota.

      pushbutton-input

      • When the light is OFF and there is a short period of pulses - then turn the light ON at the previous dimmer level.
      • When the light is ON and there is a short period of pulses - then turn the light OFF.
      • When there is a longer period of pulses (i.e., HOLD) - toggle dimming direction and then adjust the brightness level as long as the button is pressed or until the limits are reached.

      Issue 6085

      In the Data Section D at the beginning of the Script the following initialization variables may be changed:

      dim multiplier = 0..2.55 set the dimming increment value dim lower limit = range for the dimmer value for push-button operation (set according to your bulb); min 0 dim upper limit = range for the dimmer value for push-button operation (set according to your bulb); max 100 start dim level = initial dimmer level after power-up or restart; max 100

      >D
      +sw=0
      +tmp=0
      +cnt=0
      +tmr=0
      +hold=0
      +powert=0
      +slider=0
      +dim=""
      +shortprl=2 ;short press lo limit
      +shortpru=10;short press up limit
      +dimdir=0   ;dim direction 0/1
      +dimstp=2   ;dim step/speed 1 to 5
      +dimmlp=2.2 ;dim multiplier
      +dimll=15   ;dim lower limit
      +dimul=95   ;dim upper limit
      +dimval=70  ;start dim level
      +
      +>B
      +print "WiFi-Dimmer-Script-v0.2"
      +=>Counter1 0
      +=>Baudrate 9600
      +; boot sequence
      +=#senddim(dimval)
      +delay(1000)
      +=#senddim(0)
      +
      +>F
      +cnt=pc[1]
      +if chg[cnt]>0
      +; sw pressed
      +then sw=1
      +else sw=0
      +; sw not pressed
      +endif
      +
      +; 100ms timer
      +tmr+=1
      +
      +; short press
      +if sw==0
      +and tmr>shortprl
      +and tmr<shortpru
      +then
      +powert^=1
      +
      +; change light on/off
      +if powert==1
      +then
      +=#senddim(dimval)
      +else
      +=#senddim(0)
      +endif
      +endif
      +
      +
      +; long press
      +if sw>0
      +then
      +if hold==0
      +then
      +
      +; change dim direction
      +dimdir^=1
      +endif
      +if tmr>shortpru
      +then
      +hold=1
      +if powert>0
      +; dim when on & hold
      +then
      +if dimdir>0
      +then
      +
      +; increase dim level
      +dimval+=dimstp
      +if dimval>dimul  
      +then
      +
      +; upper limit
      +dimval=dimul
      +endif
      +=#senddim(dimval)
      +else
      +
      +; decrease dim level
      +dimval-=dimstp
      +if dimval<dimll
      +then
      +
      +; lower limit
      +dimval=dimll
      +endif
      +=#senddim(dimval)
      +endif
      +endif
      +endif
      +else
      +tmr=0
      +hold=0
      +endif
      +
      +>E
      +slider=Dimmer
      +
      +; slider change
      +if chg[slider]>0
      +then
      +
      +; dim according slider
      +if slider>0
      +then
      +dimval=slider
      +=#senddim(dimval)
      +else
      +powert=0
      +=#senddim(0)
      +endif
      +endif
      +
      +if pwr[1]==1
      +; on/off webui
      +then
      +powert=1
      +=#senddim(dimval)
      +else
      +powert=0
      +=#senddim(0)
      +endif
      +
      +; subroutine dim
      +#senddim(tmp)
      +dim="FF55"+hn(tmp*dimmlp)+"05DC0A"
      +=>SerialSend5 %dim%
      +=>Dimmer %tmp%
      +#
      +

      Dual display example~

      >D
      +>B
      +; load sh1106 driver
      +dt [S2/SH1106_desc.txt:]
      +; clear screen, switch to LCD font; set auto draw
      +dt [zf4s1D1]
      +dt [S1:]
      +>S
      +; switch to display 2
      +dt [S2:]
      +; show time
      +dt [x20y20t]
      +; switch back to display 1
      +dt [S1:]
      +

      read I2C example (AXP192)~

      >D
      +volt=0
      +curr=0
      +found=0
      +>B
      +; check device on I2C bus Nr.2
      +found=ia2(0x34)
      +
      +>S
      +; if found read registers, (this example takes 2ms to read both values)
      +if found>0 {
      +volt=ir(0x5a)<<4|ir(0x5b)*1.7/1000
      +curr=ir(0x58)<<4|ir(0x59)*0.625
      +}
      +
      +>W
      +; show on webui
      +Bus Voltage{m}%volt% V
      +Bus Current{m}%curr% mA
      +

      Multiplexing a single adc with CD4067 breakout~

      >D
      +; this script works with a CD4067 breakout to multiplex a single ADC channel
      +; of an ESP
      +IP=192.168.178.177
      +SB=8192
      +res=0
      +cnt=0
      +mcnt=0
      +m:mux=0 16
      +
      +>B
      +; define output pins for multiplexer
      +spinm(12 O)
      +spinm(13 O)
      +spinm(14 O)
      +spinm(15 O)
      +; define string array with 16 entries
      +res=is1(16 "")
      +is1[1]="Azalea"
      +is1[2]="Aster"
      +is1[3]="Bougainvillea"
      +is1[4]="Camellia"
      +is1[5]="Carnation"
      +is1[6]="Chrysanthemum"
      +is1[7]="Clematis"
      +is1[8]="Daffodil"
      +is1[9]="Dahlia"
      +is1[10]="Daisy"
      +is1[11]="Edelweiss"
      +is1[12]="Fuchsia"
      +is1[13]="Gladiolus"
      +is1[14]="Iris"
      +is1[15]="Lily"
      +is1[16]="Periwinkle"
      +
      +>F
      +; get adc value into array, average 4 values
      +; this is for ESP32 here on pin 32
      +mux[mcnt+1]=adc(2 32)
      +; this is for ESP8266 it has only 1 ADC input
      +; mux[mcnt+1]=adc(2)
      +mcnt+=1
      +if mcnt>=16
      +then
      +mcnt=0
      +endif
      +; set multiplexer
      +spin(12 mcnt)
      +spin(13 mcnt/2)
      +spin(14 mcnt/4)
      +spin(15 mcnt/8)
      +
      +; display web UI
      +#wsub
      +if wm==0
      +then
      +for cnt 1 16 1
      +wcs  {s}Ch %0cnt%: %is1[cnt]%{m}%mux[cnt]% %%{e}
      +next
      +endif
      +
      +#rsub
      +rap ,"CD4067":{
      +for cnt 1 16 1
      +rap "%is1[cnt]%":%mux[cnt]%
      +if cnt<16
      +then
      +rap ,
      +endif
      +next
      +rap }
      +
      +>J
      +; send to mqtt
      +; call json subroutine
      +%=#rsub
      +
      +>W
      +; call web subroutine
      +%=#wsub
      +
      these are some examples of more complex scripts to show what is possible.
      +complex scripts should be edited with the external source editor as they contain lots of comments and indents.
      +i will provide the sources later.
      +

      Internet radio~

      webradio_screenshot

      Webcam with multiple options~

      Bildschirmfoto 2022-07-16 um 08 22 11

      Energy collector~

      energy

      Energy display main menu~

      core2

      Energy display one day of database~

      oneday

      Energy display last week of database~

      week

      Energy display last weeks of database~

      lastweeks

      environment sensor~

      sensors

      timer main menu~

      timer1

      timer setup~

      timer2

      \ No newline at end of file diff --git a/Securing-your-IoT-from-hacking/index.html b/Securing-your-IoT-from-hacking/index.html new file mode 100644 index 0000000000..88d51cd658 --- /dev/null +++ b/Securing-your-IoT-from-hacking/index.html @@ -0,0 +1,49 @@ + Securing your IoT from hacking - Tasmota
      Skip to content

      Securing your IoT from hacking

      General Weaknesses~

      Whenever you add devices to your network you generate additional points of potential intrusion. This is not only valid for your mobile phones and computers, but also for you Smart TV, you Alexa, or all of your SONOFF devices (ESP8266).

      There are following potential risks you have to mitigate:

      • Someone is able to communicate with your device (Scenario 1)
      • Someone hacks your device and is able to log in into your WLAN. (why is this a problem? Scenario 2)
      • Someone hacks your device and is able to read and change any value on your MQTT server (why is this a problem? Scenario 3)
      • Someone hacks your network and can interact with your devices (why is this a problem? Scenario 4)
      • Someone hacks your device and use it for different things like mail bot or DOS (Denial of Service) device or WLAN jammer (why is this a problem? Scenario 5)

      Scenario 1~

      It is possible to set a password to the webadmin interface, however ESP8266 devices SHALL not be exposed to internet or accessible to other network clients. (e.g. users in the same network) [1] [2].

      Scenario 2~

      If someone is able to get your WLAN key, he can login into your network, if he is nearby and scan for any traffic and for any devices. Many communication is not encrypted in your WLAN by default. Therefore be part of your WLAN gives the attacker a great opportunity to screw-up the rest of your infrastructure. Also be part of your WLAN does mean, that the attacker can use your IP-Address and your traffic to do nasty things.

      Scenario 3~

      If you can hack an ESP82xx device, you might get access to the keys stored in the device. For example, the MQTT password allows you to read ALL of your devices and change any device at any time. With the information of the MQTT-Server user/password, it might be not required anymore to physically be in your WLAN. Maybe your MQTT Server is publicly accessible. Then the attacker can control your home from any place.

      Update: Beginning with version 6.0.0, passwords are not directly exposed through the serial connection or web interface in configuration mode. Therefore it is now less simple, however still possible to obtain the MQTT or WLAN password from a device. Such can be accomplished by downloading a configuration backup via the web UI of the device and decoding it using the decode-config.py script found in the Tasmota tools folder.

      Scenario 4~

      It might happen, that e.g. your Samsung SmartTV is not as secure as it should be and an attacker gets access to your network. Now he can listen to any traffic and maybe can make changes on all of your IoT devices.

      Scenario 5~

      If someone uses your device to spam mail or do a DOS attack the impact at your home is minimal. You might have more outbound traffic, but maybe you don't recognize this either. But thousands of hacked IoT devices can generate tremendous trouble even at the largest internet providers.

      I hope these five typical scenarios ( the list is not complete) give you some idea, why you should take care, even if you're not a terrorist and normally nobody is interested into hacking you personally.

      Securing your WLAN~

      That you should have a WLAN key and use WPA2 for encryption is a "no brainer". This is a minimum requirement. Now think about someone can extract the password from the device. E.g. because the device is in the garden and someone with a Laptop and some USB stuff can connect and extract information.

      The hacker will get the key. The ONLY possible preventive action to mitigate worst case scenario is to have a second WLAN, like the "FritzBox Guest WLAN". Many other routers offer similar things. This guest WLAN has no access to your private WLAN. Additionally, there are some interesting switched you can configure for the WLAN.

      At the FritzRouter you can configure "network separation". At Fritz this is done by DISABLE "The wireless devices connected with the guest access can communicate with each other". This does mean, that a device in the network can not interact with any other device in the WLAN. It can only communicate with the Internet. This simple configuration prevents any attacker to do nasty stuff on YOUR network. Now we have to take care, that the attacker is not creating a Bot-Net and sending e.g., Spam-Mails.

      Normally a device in the "guest WLAN" can use any internet service. For our IoT devices and for any new device we can create a Router rule, that ONLY MQTT is allowed to our server and any other traffic is blocked. This is a great configuration because it limits the options what a hacker can do. If you have a FritzBox following configuration has to be created to get this working:

      1. Create Profile to block all communication except MQTT and NTP Time services. Internet -> Filters -> List -> "Add Network Application" "New Protocol" (Add four rules, This will block all but UDP123 for Timeserver and 8883 for MQTT Server)

      2. TCP from any to Port 1 to 8882

      3. TCP from any to Port 8884 to 65636
      4. UDP from any to Port 1 to 122
      5. UDP from any to Port 124 to 65636

      6. Create a list of "websites" your IoT devices are allowed to access.
        Internet -> Filters -> List -> "Permitted web sites" -> EDIT

        yourserver
        +01.de.pool.ntp.org
        +
        Replace with the full qualified name of your router in the Internet.
        NTP server - Use the one you have defined in Tasmota to be the timeserver.

      7. Create a profile you can attach to your IoT devices.
        Internet -> Filters -> Access profiles -> "new Access profile"
        "Filter Web sites"
        DISABLE "Allow HTTPS queries"
        SELECT "Permit web sites (whitelist)

      Now you will probably ask two questions:

      1. How can I communicate with my MQTT Server in my personal WLAN if only traffic into the internet is allowed?
      2. How can I access the WebConsole of my devices to upload new Firmware and/or make investigations?

      The first topic will be solved by exposing your MQTT server to the Internet (no worries, can be done securely).

      The second topic has only a workaround. If you want access to your devices you need to change the configuration temporary on your router and ENABLE "The wireless devices connected with the guest access can communicate with each other". Secondly, you must login with your Laptop into the GuestWLAN to be able to communicate. If the Webserver is running you should be able to connect and upload e.g. a new firmware.

      Securing your Communication~

      In the world of IoT devices and more and more devices in a network, it is essential to use encryption ALL the time. The Tasmota project is able to enable encryption for MQTT. This is great. But it cannot enable encryption on the WebServer. This is bad. As a conclusion, the Webserver must be switched OFF all the time and only be switched ON for administrative purpose. This also disables the feature to change the Relay Status with an HTTP REST call. But this option is insecure anyway and should be avoided.

      Now let's work on the MQTT configuration. Also here an attacker can get access to user and password. To minimize the impact EVERY and really EVERY device must have a unique USER and a unique password. If you don't follow this rule the attacker get one device he can control ALL devices. With the USER/PASSWORD he now can control the one device he already holds in his hands. ok, no big deal. How to configure Mosquitto?

      In general, Tasmota stores data in stat//+ and tele//+. or cmnd//+ to control something. If we use the as username we can make some quite nice and straight forward configuration.

      Example:
      Topic: ESP_123456
      User: ESP_123456 (must be the same to Topic)
      password: 987654321

      Configuration file: /etc/mosquitte/conf.d/jp.acl

      user root
      +topic read #
      +topic write #
      +
      +pattern read cmnd/%u/#
      +pattern write stat/%u/#
      +pattern write tele/%u/#
      +

      My user root is allowed to do everything. This is used in my home-automation to control all devices and listen to all devices. The "pattern" is used for ALL other users and the %u is a substitute. The great thing is that the device can read its configuration but cannot write to it. And the status information it posts to the /status/ but is not able to read it afterward. With this minimal configuration, Tasmota devices are running.

      To add the different user to Mosquitto the following two commands work fine. There is also a re-read available, but a restart works better for me.

      sudo mosquitto_passwd -b /etc/mosquitto/conf.d/jp.pw ESP_123456 987654321
      +sudo /etc/init.d/mosquitto restart
      +

      If this is running, we switch the Mosquitto to secure communication on Port 8883 and disable all insecure options.

      /etc/mosquitto/conf.d/user.conf

      #User Config
      +password_file /etc/mosquitto/conf.d/jp.pw
      +acl_file /etc/mosquitto/conf.d/jp.acl
      +
      +allow_anonymous false
      +
      +listener 8883
      +
      +cafile   /etc/mosquitto/certs/ca.crt
      +certfile /etc/mosquitto/certs/server.crt
      +keyfile  /etc/mosquitto/certs/server.key
      +require_certificate false
      +

      How to generate the certificates in mosquitto please look at:

      SSL/TLS on Tasmota~

      TLS article explains how to set it up in Tasmota

      Self-signed-Mosquitto article explain how to set up Tasmota with certificate-based TLS authentication on a local installation of Mosquitto.

      Disable unsecured fallback WiFi (WifiManager)~

      Type WifiConfig into the tasmota console. If this parameter is set to 2, you might want to change it after completing the setup of your device since in case your Wifi SSID is not available (i.e. access point dies or WLAN jammer is used as in Scanario 5), the WiFiManager will jump into action and make your tasmota devices available using an unsecured access point.

      Some less risky options would be: 0/4/5. Currently the default WiFiConfig value is (WIFI_RETRY) which means that device retries other AP without rebooting. (For details, see Wi-Fi commands).

      Home Assistant OS MQTT Add-On~

      If you are using Home Assistant OS MQTT add-on with Tasmota integration the devices will need write access to the tasmota/discovery/# topic.

      Add the following to the ACL file (user section or general section): topic write tasmota/discovery/#.

      For completeness' sake, below is a snippet of the ACL file with the full recommended structure.

      ########
      +######## General section
      +# Commented out all general rules.
      +# topic readwrite homeassistant/#
      +# topic write tasmota/discovery/#
      +########
      +
      +########
      +######## User section
      +
      +user sonoff-living-fan-2
      +# This is used when SetOption19 1 is used.
      +topic readwrite homeassistant/#
      +# This is used when SetOption19 0 is used.
      +topic write tasmota/discovery/#
      +
      +########
      +
      +########
      +######## Pattern Section
      +# https://tasmota.github.io/docs/Securing-your-IoT-from-hacking/
      +pattern read cmnd/%u/#
      +pattern write stat/%u/#
      +pattern write tele/%u/#
      +########
      +
      \ No newline at end of file diff --git a/Self-signed-Mosquitto/index.html b/Self-signed-Mosquitto/index.html new file mode 100644 index 0000000000..c895117be4 --- /dev/null +++ b/Self-signed-Mosquitto/index.html @@ -0,0 +1,222 @@ + Self signed Mosquitto - Tasmota
      Skip to content

      Self signed Mosquitto

      This feature is not included in precompiled binaries

      To use it you must compile your build.

      The following guide will walk you through the setup of Tasmota with your own instance of Mosquitto Server with Certificate-based TLS encryption and a Self-signed CA (Certificate Authority).

      Benefits~

      The following setup provides stronger security in communication between your devices and your MQTT server.

      - No cloud usage~

      Many people might prefer cloud-based solutions, such as AWS IOT. Nonetheless, running local MQTT provides various benefits like no internet requirements, no data sharing with third parties and lower latencies.

      - Maximum security~

      This setup is designed with 'security first' in mind.

      Communication is done over TLS 1.2 tunnels, using client certificates to authenticate each device.

      Strong encryption is particularly valuable considering that ESP8266-based Tasmota devices can only connect using WPA2 with preshared keys. Because WPA2 Personal has known security weaknesses, the MQTT strong TLS configuration is encouraged in this scenario.

      Caveats~

      Certificate-based MQTT-TLS requires each Tasmota device to have its own distinct Private Key and Certificate (~800 bytes). Although you could imagine to use the same Private Key in all your devices, this is considered as a very bad practice. You are warned!

      During TLS handshake, a secondary stack of 5.3k is allocated on the heap to allow BearSSL to have enough stack room. Memory is freed at the end of the handshake. Allocating such big chunks of memory can cause issues when heap fragmentation gets too high. If you see memory going below 15KB, you may experience crashes.

      How to configure?~

      Ideally we will work on three systems: 1. CA machine: a secure, possibly air-gap system, where you generate your certificates; 2. Server Machine: the machine running your MQTT server. We will assume it is running some Debian-based distribution (i.e. Ubuntu Server), though the steps could be adapted to different OSes; 3. Compiling Machine: the machine used to compile your customized Tasmota firmware. This machine might be the same Server Machine, though I would not recommend it.

      !!! failure "Security notice" Private keys, and in particular the CA private key should reside on a secure, possibly air-gapped system. Securing your CA and procedures for managing private keys exceeds the scope of this guide, but we assume you follow best security practices.

      There are several figures below containing command sequences that need to be executed in a POSIX shell. The intention is that text in the figure windows will be selected and copied, then pasted into a terminal window. These commands will not work if pasted into a Windows command prompt.

      Linux and Windows~

      The description below is written mainly from the perspective of someone using a Linux OS. Information is also provided for those working on a Windows OS, but a Linux command shell (e.g. sh or bash) is assumed for much of the work. Cygwin is a good choice for this purpose. It's not impossible to do perform these tasks in native Windows without a POSIX shell, although that is beyond the scope of this document.

      A Cygwin installation should include the git package (Devel category) and openssl package (Net category). Additional packages will be required as discussed later if BearSSL is to be installed.

      1. Prepare your CA (on Server Machine)~

      We will use Easy-RSA for easy management of the CA and certificates. Some modification are required to match our configuration.

      1.1. Prepare Easy-RSA :~

      Get a copy of Easy-RSA and add a reduced x509 extension definition for Tasmota. Certificates obtained using standard client definitions are too big and results in failure when storing them on Tasmota devices.

      git clone https://github.com/OpenVPN/easy-rsa.git
      +cd easy-rsa/easyrsa3
      +
      +# Define reduced x509 extension for Tasmota
      +cat <<EOF > x509-types/tasmota
      +# X509 extensions for a Tasmota client
      +basicConstraints = critical,CA:FALSE
      +subjectKeyIdentifier = hash
      +authorityKeyIdentifier = keyid:always
      +keyUsage = critical,digitalSignature
      +EOF
      +
      Windows users may have trouble running EasyRSA natively. If that happens, it's also possible to install Cygwin, and work from a Cygwin terminal window. One note of caution: The easyrsa shell script may wind up with the wrong line endings if git is not configured to checkout line endings "as is". When this happens, the shell script will not run in Cygwin, and this problem may be fixed by using the tr program to delete carriage returns in the script file:
      mv easyrsa tmprsa
      +tr -d '\r' <tmprsa >easyrsa
      +

      1.2. Define your certificate information~

      The commands below may be copied and pasted into a terminal window, then the resulting file, vars edited as appropriate.

      # Define your info
      +cat <<EOF > vars
      +# Define X509 DN mode.
      +# This is used to adjust what elements are included in the Subject field as the DN
      +# (this is the "Distinguished Name.")
      +# Note that in cn_only mode the Organizational fields further below aren't used.
      +#
      +# Choices are:
      +#   cn_only  - use just a CN value
      +#   org      - use the "traditional" Country/Province/City/Org/OU/email/CN format
      +
      +set_var EASYRSA_DN "org"
      +
      +# Organizational fields (used with 'org' mode and ignored in 'cn_only' mode.)
      +# These are the default values for fields which will be placed in the
      +# certificate.  Don't leave any of these fields blank, although interactively
      +# you may omit any specific field by typing the "." symbol (not valid for
      +# email.)
      +
      +set_var EASYRSA_REQ_COUNTRY "UK"
      +set_var EASYRSA_REQ_PROVINCE "London"
      +set_var EASYRSA_REQ_CITY "London"
      +set_var EASYRSA_REQ_ORG "myorg"
      +set_var EASYRSA_REQ_EMAIL "info@myorg.com"
      +set_var EASYRSA_REQ_OU "MQTT"
      +
      +# Choose a size in bits for your keypairs. The recommended value is 2048.  Using
      +# 2048-bit keys is considered more than sufficient for many years into the
      +# future. Larger keysizes will slow down TLS negotiation and make key/DH param
      +# generation take much longer. Values up to 4096 should be accepted by most
      +# software. Only used when the crypto alg is rsa (see below.)
      +
      +set_var EASYRSA_KEY_SIZE 2048
      +
      +# The default crypto mode is rsa; ec can enable elliptic curve support.
      +# Note that not all software supports ECC, so use care when enabling it.
      +# Choices for crypto alg are: (each in lower-case)
      +#  * rsa
      +#  * ec
      +#  * ed
      +
      +set_var EASYRSA_ALGO rsa
      +
      +# Define the named curve, used in ec & ed modes:
      +
      +set_var EASYRSA_CURVE prime256v1
      +# In how many days should the root CA key expire?
      +
      +set_var EASYRSA_CA_EXPIRE 3650
      +
      +# In how many days should certificates expire?
      +
      +set_var EASYRSA_CERT_EXPIRE 1800
      +# Cryptographic digest to use.
      +# Do not change this default unless you understand the security implications.
      +# Valid choices include: md5, sha1, sha256, sha224, sha384, sha512
      +
      +#set_var EASYRSA_DIGEST "sha256"
      +EOF
      +

      This creates a configuration file named vars -- the default file which the easyrsa shell script looks for.

      If the configuration is to be changed there are two options. First, vars can simply be edited to reflect the new configuration. However, if multiple configurations are often used, it may be easier to use a different file for each configuration, and the configuration specified on the command line. For example, to use a configuration file named vars3, the command would look like this (the config file spec must come before other arguments):

      ./easyrsa --vars=./vars3 <remaining arguments>
      +

      Note that the file is specified as ./<filename>, not <filename>. This required because the specified file will be sourced in the EasyRSA shell script, and it's assumed that the current directory is not part of the search path (that would be a security concern).

      1.3. Initialize and generate the CA and the server certificates:~

      When generating the server (aka broker) certificate, it is crucial that the Common Name (CN) be set correctly. Failing to do this will result in Tasmota devices refusing to make TLS connections to the server.

      Each Tasmota device needs to be configured with the host name of the server. This is done via the MQTT_HOST macro in user_config_override.h, and/or in the device's MQTT Configuration web page. The host name string must meet two requirements: - The Tasmota device must be able to resolve the name, which might require access to a DNS server. - This string must match the Common Name (CN) in the broker's certificate -- exactly.

      Consider a situation where the device is running on an isolated WiFi network with no access to a DNS server. In this case, it may be necessary to specify the MQTT Host as a numeric IP address (e.g. 192.168.2.3). For this example, the CN in the host's certificate must be the string 192.168.2.3.

      To generate the root CA and server certificates, issue these commands. This example assumes the server's hostname and server certificate CN is mqtt.myorg.com.

      # Reset PKI
      +./easyrsa init-pki
      +# Build CA following instructions
      +./easyrsa build-ca
      +# Request server certificate following instructions
      +./easyrsa gen-req mqtt.myorg.com
      +# Sign certificate for server
      +./easyrsa sign-req server mqtt.myorg.com
      +

      1.4. Change key types (EC is required for tasmota devices)~

      This example shows editing of the vars file, although a second configuration file could be created for EC keys as explained previously.

      sed -i '/^set_var\ EASYRSA_ALGO/ s/rsa/ec/' vars
      +

      1.5. Copy the following files to the Server Machine~

      ./pki/ca.crt --> CA certificate file
      +./pki/issued/mqtt.myorg.com.crt --> Server certificate file
      +./pki/private/mqtt.myorg.com.key --> Server private Key file
      +

      1.6. (Optional step for full certificate validation)~

      • Copy ./pki/ca.crtto your Compiling Machine
      • Install and build BearSSL (see below for help with building BearSSL on Windows machines).
        git clone https://www.bearssl.org/git/BearSSL
        +cd BearSSL
        +make tools
        +
      • Convert the root certificate into a format suitable for inclusion in the Tasmota build. This be will easier if the brssl (brssl.exe in Cygwin) executable is copied into the the easyrsa3 directory first. Then, these two commands may be executed from the easyrsa3 directory verbatim to generate the required header files.
      ./brssl ta pki/ca.crt | sed -e 's/TA0/PROGMEM TA0/' -e '/br_x509/,+999 d' > local_ca_data.h
      +./brssl ta pki/ca.crt | sed -e '1,/br_x509/ d' -e '/};/,+999 d' >local_ca_descriptor.h
      +

      2. Configure your Tasmotabuild (on Compiling Machine)~

      Refer to your preferred way to custom compile Tasmota. Modify the default configuration following one of 2.1 or 2.2 solutions (Easier or Advanced):

      2.1. Easier: Using fingerprint validation~

      Add the following to user_config_override.h:

      #ifndef USE_MQTT_TLS
      +#define USE_MQTT_TLS
      +//  #define USE_MQTT_TLS_CA_CERT               // Force full CA validation instead of fingerprints, slower, but simpler to use.  (+2.2k code, +1.9k mem during connection handshake)
      +#define USE_MQTT_AWS_IOT                       // This includes the LetsEncrypt CA in tasmota_ca.ino for verifying server certificates
      +#define USE_MQTT_TLS_FORCE_EC_CIPHER           // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem)
      +#define MQTT_FINGERPRINT1      "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"  // [MqttFingerprint1] (auto-learn)
      +#define MQTT_FINGERPRINT2      "DA 39 A3 EE 5E 6B 4B 0D 32 55 BF EF 95 60 18 90 AF D8 07 09"  // [MqttFingerprint2] (invalid)
      +#endif
      +

      2.2. Advanced: Using Full certificate validation~

      Add the following to user_config_override.h:

      #ifndef USE_MQTT_TLS
      +#define USE_MQTT_TLS
      +#define USE_MQTT_TLS_CA_CERT                   // Force full CA validation instead of fingerprints, slower, but simpler to use.  (+2.2k code, +1.9k mem during connection handshake)
      +#define USE_MQTT_AWS_IOT                       // This will include LetsEncrypt CA, as well as our CA, in tasmota_ca.ino for verifying server certificates
      +#define USE_MQTT_TLS_FORCE_EC_CIPHER           // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem)
      +#define INCLUDE_LOCAL_CERT
      +#endif
      +
      Copy or move the files created in step 1.6 above (local_ca_data.h and local_ca_descriptor.h) to the $TASMOTAROOT/tasmota/include directory. If desired, the two built-in certificates for Let's Encrypt and Amazon AWS may be omitted from the build by defining these macros in user_config_override.h:

      #define OMIT_LETS_ENCRYPT_CERT
      +#define OMIT_AWS_CERT
      +

      3. Build your Tasmota Binaries, flash and configure your devices~

      Initially at least, build the binaries with the web server enabled. Pull up the web page on each device and check the configuration. In particular check the following settings.

      • WiFi Host Name
      • MQTT Host -- must be resolvable, and match the Common Name on the server's certificate
      • MQTT Port -- this often does not come up as defined in the configuration file, and must be changed to 8883
      • MQTT Topic

      4. Configure your network~

      The Common Name (CN) in the server's certificate will either be a resolvable name (like mqtt.myorg.com), or the IP address string for the server (e.g. 192.168.2.3). The host name assigned to the server must match this CN, unless an IP address string is used in the server certificate CN, in which case the host name can be anything. These next two items are only necessary where the CN is not an IP address string.

      • Configure your router to resolve the Tasmota device's MQTT Host Name (e.g. mqtt.myorg.com) to your Server Machine.
      • Configure your Server Machine hostname to the same name (e.g. mqtt.myorg.com).

      In all cases, check these Tasmota guidelines about Securing your IoT.

      5. Install and configure Mosquitto on your server~

      Install Mosquitto

      Linux~

      sudo apt-get install Mosquitto
      +

      Windows~

      Download and run the Windows installer from https://www.mosquitto.org/. Mosquitto may be installed either as a program or service. The main difference is in how mosquitto is started. As a program, it must be started by a user every time, but as a service it can be automatically started by the OS during boot.

      5.1 Configuration~

      Copy the files from CA Machine to the following locations on a Linux server machine:

      /etc/mosquitto/ca_certificates/ca.crt
      +/etc/mosquitto/certs/mqtt.myorg.com.crt
      +/etc/mosquitto/certs/mqtt.myorg.com.key
      +

      and to here on a Windows server machine (requires Administrative privileges):

      C:\Program Files\mosquitto\ca.crt
      +C:\Program Files\mosquitto\mqtt.myorg.com.crt
      +C:\Program Files\mosquitto\mqtt.myorg.com.key
      +
      On Windows, if preferred, one or two subdirectories within the mosquitto installation directory can be created to hold the certificates. That might make it easier to restrict access to a private key in plain text form.

      There are two options for configuring the server's private key. It can be converted to plain text form (.pem) as shown below, or left encrypted (.key). If left encrypted, the password will need to be entered by hand every time the server is started. This will not be feasible if mosquitto is to be run as a service. Security risks can be minimzed by setting tight permissions on the files, as shown below.

      Convert the private key to plain text format like this:

      openssl rsa -in mqtt.myorg.com.key -out mqtt.myorg.com.pem

      File Permissions: Linux~

      Ensure the files have owner mosquitto:mosquitto and permissions -r--------. Also ensure ca_certificates and certs directories have owner mosquitto:mosquitto and permissions dr-x------.

      File Permissions: Windows~

      It should be possible to configure permissions so that only the SYSTEM user can read the private key file, and when mosquitto is run as a service, it runs under the SYSTEM account. How to do this is currently beyond the scope of this guide.

      5.3 Configure and start the server~

      Edit /etc/mosquitto/conf.d/default.conf as following:

      protocol mqtt
      +allow_anonymous false
      +listener 8884
      +socket_domain ipv4
      +# Certs and Keys
      +cafile /etc/mosquitto/ca_certificates/ca.crt
      +certfile /etc/mosquitto/certs/mqtt.myorg.com.crt
      +keyfile /etc/mosquitto/certs/mqtt.myorg.com.pem
      +require_certificate true
      +use_identity_as_username true
      +

      On a Windows machine, the configuration file is C:\Program Files\mosquitto\mosquitto.conf, and the paths to the certificate and key files should be set accordingly using standard Windows path syntax.

      To start Mosquitto on Linux:

      sudo service mosquitto Start
      +

      To start Mosquitto on Windows, either use the services snap-in (services.msc), or from an Administrator command prompt:

      net start mosquitto
      +

      6. - Generate and configure certificates for your devices~

      !!! failure "Repeated step" Repeat the following 6.x steps once for every device, changing tasmota_name for each device. You will be prompted for a private key password for each device. After entering the new password (twice for verification), you will also be prompted for the private key password of the root CA certificate.

      6.1 Generate the client certificates~

      export TAS=tasmota_name
      +# Request tasmota certs (will ask for password)
      +./easyrsa gen-req $TAS
      +# Sign certificate for Tasmota
      +./easyrsa sign-req tasmota $TAS
      +

      6.2 Convert certificate keys to Tasmota compatible format~

      The new certificate must be converted to Tasmota commands which can be entered into the device's web console. Credentials are composed of two distinct parts:

      • Private Key - this is the secret that will allow your device to prove its identity, and consists of 32 bytes (256 bits). Consider this as sensitive as a password.
      • Public Key - this allows others to encrypt messages which can only be decrypted with the Private Key, and contains 256 bytes (2048 bits).

      Both of these must be loaded into flash in the Tasmota device. This is done by entering TLSKey commands in the device's web console.

      This step must be performed on the machine where the device certificates were created, from within the easyrsa3 directory. The following commands will generate a shell script, gen-tlskeys, which will perform the necessary work.

      cat >gen-tlskeys <<'EOF'
      +# Decrypt private key (will ask for a password), then extract TLSKey1 (private) and TLSKey2 (public) values
      +if [ "$#" -ne 1 ] ; then
      +    echo "Usage: gen-tasmota-cert <device-certificate-name>"
      +    exit 1
      +fi
      +if [ ! -f pki/private/$1.key ] ; then
      +    echo "Could not find private key file pki/private/$1.key"
      +    exit 1
      +fi
      +if [ ! -f pki/issued/$1.crt ] ; then
      +    echo "Could not find public key file pki/issued/$1.crt"
      +    exit 1
      +fi
      +openssl ec -in ./pki/private/$1.key -outform PEM | \
      +openssl ec -inform PEM -outform DER | openssl asn1parse -inform DER | \
      +head -3 | tail -1 | awk -F':' '{ print $4 }' | xxd -r -p | base64 | \
      +echo -e "----\n\nCopy the following commands and paste them into the device's web console\n\n---\n\nTLSKey1 $(</dev/stdin)" && \
      +openssl x509 -in ./pki/issued/$1.crt -inform PEM -outform DER | \
      +openssl base64 -e -in - -A|echo -e "\n\nTlskey2 $(</dev/stdin)"
      +EOF
      +chmod 755 gen-tlskeys
      +

      Now, for each device, simply enter this command. As above, the variable ($TAS) is set to the name of the device's certificate. Output will consist of two Tasmota device commands printed to the terminal window; these must be copied and pasted into the device's web console.

      ./gen-tlskeys $TAS
      +

      The TLSKey1 command contains the device's private key in plain text (unencrypted) format, so don't keep it around any longer than necessary.

      6.3 Access your device's web console and configure the keys.~

      Run the TLSKey1 and TLSKey2 commands as obtained in the previous output. Open each of the files, copy the text and paste it into the web console.

      6.4 Verify the device is connecting as expected.~

      Building BearSSL on Windows machines~

      This can be a challenging task, and a method to accomplish this through Cygwin is described here.

      Here are the issues that need to be fixed before BearSSL will build under Cygwin. Many of them are related to the differences between Windows and Linux when it comes to programming socket I/O. - Commands for compiling and linking are gcc, not cc - Additional compiler flags are required to compile and link socket I/O code - A Windows socket library (libws2_32) must be included in the link edit step when building the brssl executable - A local implementation of a missing function (inet_ntop) must be added to the build

      The current git master branch of BearSSL is required -- the 0.6 version available as a gzipped tar archive will not work. An up-to-date version of gcc in Cygwin is also required -- old versions may experience an internal compiler error when building BearSSL.

      Start by installing Cygwin, selecting the gcc and make packages in the Devel category, and the openssl package in the Net category.

      Open a Cygwin terminal window and change to the directory where BearSSL source was cloned. To access a Windows letter drive such as F:\somepath in Cygwin, use the path /cygdrive/f/somepath.

      All of the necessary patches are easily made by copying and pasting this set of commands into the Cygwin terminal window. This will create files needed to patch the BearSSL build. First, change to the top level BearSSL directory in the Cytwin window. After selecting the text in the window below, click in the Cygwin window and hit Control-Insert to paste, or right-click in the window and select Paste from the drop-down menu. You may need to hit Enter after doing this.

      cat >edit-Unix-mk.sed <<'EOF'
      +1 a \# Modified for building on Cygwin systems
      +/^CC.*=.*cc/ c \CC = gcc
      +/^LDDLL.*=.*cc/ c \LDDLL = gcc
      +/^LD.*=.*cc/ c \LD = gcc
      +/^CFLAGS.*=/ s/-fPIC/-DWINVER=0x0501 -DEWOULDBLOCK=EAGAIN/
      +/^LDOUT.*=/ a \BRSSL_EXT_LIBS = -lws2_32
      +EOF
      +
      +cat >extraTargets <<'EOF'
      +
      +$(OBJDIR)$Pinet_ntop$O: tools$Pinet_ntop.c $(HEADERSTOOLS)
      +    $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pinet_ntop$O tools$Pinet_ntop.c
      +
      +$(BRSSL): $(BEARSSLLIB) $(OBJBRSSL)
      +    $(LD) $(LDFLAGS) $(LDOUT)$(BRSSL) $(OBJBRSSL) $(BEARSSLLIB) $(BRSSL_EXT_LIBS)
      +EOF
      +
      +cat >tools/inet_ntop.c <<'EOF'
      +#ifdef _WIN32
      +#include <stdio.h>
      +#include <stdlib.h>
      +#include <string.h>
      +#include <stdint.h>
      +#include <errno.h>
      +#include <signal.h>
      +#include <winsock2.h>
      +#include <ws2tcpip.h>
      +const char *inet_ntop(int af, const void *src, char *dst, int cnt)
      +{
      +        if (af == AF_INET)
      +        {
      +                struct sockaddr_in in;
      +                memset(&in, 0, sizeof(in));
      +                in.sin_family = AF_INET;
      +                memcpy(&in.sin_addr, src, sizeof(struct in_addr));
      +                getnameinfo((struct sockaddr *)&in, sizeof(struct
      +sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
      +                return dst;
      +        }
      +        else if (af == AF_INET6)
      +        {
      +                struct sockaddr_in6 in;
      +                memset(&in, 0, sizeof(in));
      +                in.sin6_family = AF_INET6;
      +                memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
      +                getnameinfo((struct sockaddr *)&in, sizeof(struct
      +sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
      +                return dst;
      +        }
      +        return NULL;
      +}
      +#endif
      +EOF
      +
      +cat >patch-bearssl <<'EOF'
      +sed -f edit-Unix-mk.sed conf/Unix.mk >conf/Cygwin.mk
      +sed -e '/^OBJBRSSL = / a \ $(OBJDIR)$Pinet_ntop$O \\' mk/Rules.mk >Rules.tmp
      +cat Rules.tmp extraTargets >mk/Rules.mk
      +rm -f Rules.tmp
      +cp -f tools/brssl.h brssl.tmp
      +sed -e '/^#include.*bearssl\.h/ a \const char *inet_ntop(int,const void*,char*,int);' brssl.tmp >tools/brssl.h
      +rm -f brssl.tmp
      +EOF
      +chmod 755 patch-bearssl
      +

      Check to make sure that the file extraTargets contains a leading tab character, not spaces on the $(CC) and $(LD) lines. Executing the following commands should get the job done:

      ./patch-bearssl
      +make tools CONF=Cygwin
      +

      The resulting brssl.exe file will be found in the build directory, and should be copied to the top level EasyRSA directory.

      \ No newline at end of file diff --git a/Sensor-API/index.html b/Sensor-API/index.html new file mode 100644 index 0000000000..7e9f90f3f3 --- /dev/null +++ b/Sensor-API/index.html @@ -0,0 +1,554 @@ + Sensor API - Tasmota
      Skip to content

      Sensor API

      Tasmota sensor API documentation for sensor driver development.

      Important things to consider~

      • There are several I2C sensor examples you can take from the development codebase when writing your own and you are encouraged to do this as it is a quick and easy way to see how things fit together.
      • The Tasmota firmware is essentially intended for ESP8266/ESP8285 Wi-Fi SoC based devices and commits to the main development branch will be subject to review based on whether or not what you intend to develop or add to the existing code is relevant to the general ESP device users.
      • That being said, there is a lot of development going into the firmware which extends the functionality of standard off the shelf Sonoff devices. The firmware in itself is also useful for boards such as the WeMos ESP82xx boards. More technically inclined individuals who use generic ESP82xx modules in their own circuits to provide more access to pins and the ability to add more sensors and hardware external to the device or the generic ESP82xx module circuits can also take advantage of Tasmota.
      • The resources on the ESP82xx are finite. Most devices ship with 1MByte SPI flash which means for the generic device users, the code generally needs to be less than 502KBytes to ensure that OTA (Over The Air) flash functionality (which is the main reason why people use this firmware) remains available. RAM is also limited to an absolute maximum of 80KBytes. This memory is divided into heap (used by global variables and Strings) and stack (used by local variables) where stack space is just 4KBytes.
      • Given the above resource constraints it's important to keep your code as small as possible, as fast running as possible, and use as little RAM as possible.
      • You need to think about these resource constraints all the time whilst doing any development you wish to add to the firmware functionality - Face the fact that microcontroller development isn't as close a relative to standard computer programming as you'd expect.
      • You will be adding code to an existing framework which requires you to adhere to some simple but strict rules such as not having any infinite loops like you would have in your generic Arduino code and try to avoid using the delay() functions when writing your code as this will cause the entire firmware to be subjected to the delays you have added - Infinite loops will cause the firmware to lock up completely!
      • If your sensor has configuration options please make these available by using the SensorXX framework which is already incorporated in the base code - This may not stop you from using a web-based configuration interface but since web-based configuration takes up a lot of code space in flash it is very important to make this optional by means of a compiler directive or a #define in the configuration file and as such something you need to keep in mind during your development and debugging - The more progressively optional additional features are in your driver the smaller the basic codebase can be for minimalist implementations.
      • Whilst developing drivers for devices that use the I2C bus always consider other devices already supported in the codebase which may use the same address range. This could mean you need to find a unique way of differentiating your device detection from other devices on the same address range (e.g. querying a model-specific register) and/or disabling by #undef existing devices if yours is selected with a #define statement and in such cases always provide a warning to the user during compile time using the #warning pragma such as including #warning **** Turned off conflicting drivers SHT and VEML6070 **** in your code.
      • DO NOT ADD WEB INTERFACE FOR SENSOR CONFIGURATION if your sensor requires additional user configuration. The reason for this is the additional program memory required but most importantly the amount of RAM required to even create minimal user interfaces. Running out of RAM during runtime will lead to abnormal behaviour of your driver and/or other drivers or the entire firmware! See sensors such as the MCP23008/MCP23017 driver on more information on how to implement SensorXX commands instead!
      • While developing you might want to enable additional debugging provided by file xdrv_99_debug.ino using #define USE_DEBUG_DRIVER which provides some commands for managing configuration settings and CPU timing. In addition you can enable define PROFILE_XSNS_SENSOR_EVERY_SECOND to profile your drivers duration.
      • Do not assume others will know immediately how to use your addition and know that you will need to write a Wiki for it in the end.

      Managing a Forked Branch~

      If you plan to submit a PR bigger than a simple change in one file, here is a short intro about how to do a clean PR.

      • fork the Tasmota repository in Github
      • git clone https://github.com/<github_user>/Tasmota.git and work on your local copy
      • git remote add upstream https://github.com/arendst/Tasmota.git
      • git checkout development
      • git checkout -b <temp_branch> to create a working branch where you can push commits
      • git push --set-upstream origin <temp_branch>
      • work on your local version and push as many commits as you want

      When you think it is ready to merge and submit a PR:

      • git checkout development to go back to the main branch
      • git pull upstream development to update all the latest changes
      • git push to update your fork
      • git checkout -b <pr_branch> to create a new branch for the final PR
      • git push --set-upstream origin <pr_branch>
      • Merge the edits but be sure to remove the history of your local commits
      • git merge --squash <temp_branch>
      • git commit -m "Message"

      Now you have a clean single commit from which you can create the PR on the Tasmota Github.

      Directory/file structure~

      Sensor libraries are located in the lib/ directory. Sensor drivers are located in the tasmota/ directory. The filename of the sensor driver is xsns_<driver_ID>_<driver_name>.ino, e.g. xsns_05_ds18b20.ino where <driver_ID> is a unique number between 01 and 90 and <driver_name> is a human-readable name of the driver.

      Using generic libraries from external sources for sensors should be avoided as far as possible as they usually include code for other platforms and are not always written in an optimized way.

      API structure~

      Pre-processor directives~

      Conditional compiling of a sensor driver is achieved by adding a pre-processor directive of the scheme USE_<driver_name> in my_user_config.h. Accordingly the driver code has to be wrapped in #ifdef USE_<driver_name> ... #endif // USE_<driver_name>. Any Sensor driver must contain a pre-processor directive defining the driver ID by the scheme #define XSNS_<driver_ID>.

      Callback function~

      Any sensor driver needs a callback function following the scheme

      // Conditional compilation of driver
      +#ifdef USE_<driver_name>
      +
      +// Define driver ID
      +#define XSNS_<driver_ID>  <driver_ID>
      +
      +/**
      + * The callback function Xsns<driver_ID>() interfaces Tasmota with the sensor driver.
      + *
      + * It provides the Tasmota callback IDs.
      + *
      + * @param   byte    callback_id  Tasmota function ID.
      + * @return  boolean              Return value.
      + * @pre     None.
      + * @post    None.
      + *
      + */
      +boolean Xsns<driverID>(byte callback_id) {
      +
      +  // Set return value to `false`
      +  boolean result = false;
      +
      +  // Check if I2C interface mode
      +// if(i2c_flg) {
      +
      +  // Check which callback ID is called by Tasmota
      +  switch (callback_id) {
      +    case FUNC_INIT:
      +      break;
      +    case FUNC_EVERY_50_MSECOND:
      +      break;
      +    case FUNC_EVERY_SECOND:
      +      break;
      +    case FUNC_JSON_APPEND:
      +      break;
      +#ifdef USE_WEBSERVER
      +    case FUNC_WEB_APPEND:
      +      break;
      +#endif // USE_WEBSERVER
      +    case FUNC_SAVE_BEFORE_RESTART:
      +      break;
      +    case FUNC_COMMAND:
      +      break;
      +  }
      +// } // if(i2c_flg)
      +
      +  // Return boolean result
      +  return result;
      +}
      +#endif // USE_<driver_name>
      +

      Callback IDs~

      FUNC_INIT

      This callback ID is called when sensor drivers should be initialized.

      FUNC_EVERY_50_MSECOND

      This callback ID is called every 50 milliseconds, e.g. for near real-time operation

      FUNC_EVERY_SECOND

      This callback ID is called every second.

      It can be useful for anything that you need to do on a per second basis and is commonly used as an entry point to detect a driver or initialize an externally driven device such as a sensor, relay board or other forms of input/output required by your driver.

      You would normally want to make sure you've detected and initialised before it is used by JSON_APPEND, etc. so that it's ready to serve data.

      The generally accepted way to use this would be to detect your sensor and once this is done set a sensor value accordingly so that the function does not use unnecessary resources during future calls, for example:

      void MySensorDetect()
      +{
      +  if (MySensorDetected) { return; }
      +  /*
      +   * Perform the code which needs to be completed to
      +   * detect your sensor and then set MySensorDetected to
      +   * a non-zero value which will prevent this section
      +   * of your code to re-run every time the function is
      +   * called.
      +   * 
      +   * Under normal circumstances you'd not need to do
      +   * re-detect or initialise your sensor once it has been
      +   * done
      +   */
      +}
      +

      Setting a flag that the driver was successful in detecting the attached chip/board via I2C or SPI will prevent it from continuously trying to initialize an already initialized device.

      When writing your function responsible for detecting an externally connected I2C device try to create a method by which you read or write to specific registers that would be applicable to that specific I2C device only as to confirm a positive detect for the device. If this is not done extensively it will lead to some drivers getting false detects for a different device type simply because it shares the same I2C address.

      Unless your driver is specifically going to use the entire array of addresses provisioned by the manufacturer please consider using a #define USE_MYCHIPNAME_ADDR in the my_user_config.h so that the user may specify the address on which to expect the device. This is of course only applicable to drivers that are not enabled by default in any of the pre-built binaries.

      I2C address auto-detection example

      #define MPR121_I2C_ADDR_1ST  0x5A    /** 1st I2C address of sensor model **/
      +#define MPR121_I2C_ADDR_NUM  4       /** Number of sensors/I2C addresses **/
      +#define MPR121_I2C_ID_REG    0x5D    /** Sensor model specific ID register **/
      +#define MPR121_I2C_ID_VAL    0x24    /** Sensor model specific ID register value **/
      +
      +/* Sensor data struct type declaration/default definition */
      +typedef struct {
      +    bool connected    = false;     /** Status if sensor is connected at I2C address */
      +    bool running      = false;     /** Running state of sensor */
      +        .
      +        .
      +        .
      +} mpr121;
      +
      +// Declare array of sensor data structs
      +mpr121 mpr121[MPR121_I2C_ADDR_NUM];
      +
      +// Sensor specific init function
      +void mpr121_init() {
      +    bool anyConnected = false;
      +    // Loop through I2C addresses
      +    for (uint8_t i = 0; i < MPR121_I2C_ADDR_NUM); i++) {
      +
      +        // Check if sensor is connected on I2C address
      +        mpr121[i].connected = (MPR121_I2C_ID_VAL == I2cRead8(MPR121_I2C_ADDR_1ST + i, MPR121_I2C_ID_REG);
      +        if(mpr121[i].connected) {
      +            anyConnected = true;
      +
      +            // Log sensor found
      +            snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121-%d " D_FOUND_AT " 0x%X"), i, MPR121_I2C_ADDR_1ST + i);
      +            AddLog(LOG_LEVEL_INFO);
      +
      +            // Initialize sensor
      +            .
      +            .
      +            .
      +
      +            // Set running to true
      +            mpr121[i].running = true;
      +        }
      +    }
      +    if(!anyConnected){
      +        snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121: No sensors found"));
      +        AddLog(LOG_LEVEL_INFO);
      +    }
      +}
      +

      Four advanced methods to use FUNC_EVERY_SECOND (Food for thought) :

      • If a sensor needs an action which takes a long time, like more than 100mS, the action will be started here for a future follow-up. Using the uptime variable for testing like (uptime &1) will happen every 2 seconds. An example is the DS18B20 driver where readings (conversions they call it) can take up to 800mS from the initial request.
      • If a sensor needed the previous action it is now time to gather the information and store it in a safe place to be used by FUNC_JSON_APPEND and/or FUNC_WEB_APPEND. Using the else function of the previous test (uptime &1) will happen every 2 seconds too but just 1 second later than the previous action.
      • If a sensor does not respond for 10 times the sensor detection flag could be reset which will stop further processing until the sensor is re-detected. This is currently not being used actively as some users complain about disappearing sensors for whatever reason - Could be hardware related but easier to make Tasmota a little more flexible.
      • Making re-detection of a sensor possible by executing this once every 100 seconds (94 == (uptime %100)) a re-attached sensor can be detected without a restart of Tasmota. The 94 given in this example should be different for every sensor driver to make sure not all sensors start detection at the same time. Using the drivers index number should be a good starting point.

      FUNC_PREP_BEFORE_TELEPERIOD

      NOTE: This callback ID is deprecated as sensors should prepare for more regular updates due to "realtime" rule execution. Use FUNC_EVERY_SECOND instead. See examples used in xsns_05_ds18x20.ino and xsns_09_bmp.ino where updated sensor data is stored in preparation to calls to FUNC_JSON_APPEND and FUNC_WEB_APPEND.

      FUNC_JSON_APPEND

      This callback ID is called when TelePeriod is due to append telemetry data to the MQTT JSON string or at approximately every 2 seconds when a rule is checked, e.g.

      snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i,j));
      +

      FUNC_WEB_APPEND

      This callback ID is called every millisecond when HTML code should be added to the Tasmota web-interface main page, e.g.,

      snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{s}MPR121%c Button%d{m}%d{e}"), mqtt_data, pS->id[i], j, BITC(i,j));
      +
      It should be wrapped in #ifdef USE_WEBSERVER ... #endif // USE_WEBSERVER

      FUNC_SAVE_BEFORE_RESTART

      This callback ID is called to allow a sensor to prepare for saving configuration changes. To be used to save volatile data just before a restart. Variables can be appended to struct SYSCFG {} Settings in file tasmota/settings.h.

      FUNC_COMMAND

      This callback ID is called when a sensor specific command Sensor<xx> or Driver<xx> is executed where xx is the sensor index.

              case FUNC_COMMAND:
      +            if (XSNS_<driver_ID> == XdrvMailbox.index) {
      +               result = <driver_name>Command() { ... };  // Return true on success
      +            }
      +            break;
      +
      // Data struct of FUNC_COMMAND ID
      +struct XDRVMAILBOX {
      +  uint16_t      valid;      // ???
      +  uint16_t      index;      // Sensor index
      +  uint16_t      data_len;   // Length of command string
      +  uint16_t      payload16;  // 16 bit unsigned int of payload if it could be converted, otherwise 0
      +  int16_t       payload;    // 16 bit signed int of payload if it could be converted, otherwise 0
      +  uint8_t       grpflg;     // ???
      +  uint8_t       notused;    // ???
      +  char         *topic;      // Command topic
      +  char         *data;       // Command string/value - length of which is defined by data_len
      +} XdrvMailbox;
      +

      If your driver needs to accept multiple parameters for SensorXX and/or DriverXX please consider using comma delimited formatting and use the already written subStr() function declared in support.ino to parse through the parameters you need.

      An example of those could be

      SensorXX reset // The reset parameter may be intercepted using:
      +if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"RESET")) { // Note 1 used for param number
      +    MyDriverName_Reset(); 
      +    return serviced;
      +}
      +
      Or in the case of multiple parameters
      SensorXX mode,1
      +if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"MODE")) { // Note 1 used for param number
      +  uint8_t mode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2);  // Note 2 used for param number
      +}
      +

      Useful functions~

      MQTT~


      void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic, boolean retained)
      +
      This function publishes MQTT messages immediately, e.g.,
      snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i,j));
      +MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data);
      +

      Logging~

      void AddLog(byte loglevel)
      +
      This function adds log messages stored in log_data to the local logging system, e.g.
      snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121(%c) " D_FOUND_AT " 0x%X"), pS->id[i], pS->i2c_addr[i]);
      +AddLog(LOG_LEVEL_INFO);
      +
      void AddLogSerial(byte loglevel)
      +
      This function adds a log message to the local logging system dumping the serial buffer as hex information, e.g.
      AddLogSerial(LOG_LEVEL_INFO);
      +
      void AddLogMissed(char *sensor, uint8_t misses)
      +
      This function adds a log message to the local logging system about missed sensor reads.

      I2C Interface~

      bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg)
      +
      bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg)
      +
      bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg)
      +
      bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg)
      +
      bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg)
      +
      bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg)
      +
      bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size)
      +
      These functions return true if 1, 2, 3 or size bytes can be read from the I2C address addr and register reg into *data. Functions with a S read signed data types while functions without a S read unsigned data types. Functions with LE read little-endian byte order while functions without LE read machine byte order.

      uint8_t I2cRead8(uint8_t addr, uint8_t reg)
      +
      uint16_t I2cRead16(uint8_t addr, uint8_t reg)
      +
      int16_t I2cReadS16(uint8_t addr, uint8_t reg)
      +
      uint16_t I2cRead16LE(uint8_t addr, uint8_t reg)
      +
      int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg)
      +
      int32_t I2cRead24(uint8_t addr, uint8_t reg)
      +
      These functions return 1, 2 or 3 bytes from the I2C address addr and register reg. Functions with a S read signed data types while functions without a S read unsigned data types. Functions with LE read little endian byte order while functions without LE read machine byte order.

      bool I2cWrite8(uint8_t addr, uint8_t reg, uint8_t val)
      +
      bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val)
      +
      bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size)
      +
      These functions return true after successfully writing 1, 2 or size bytes to the I2C address addr and register reg.

      int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len)
      +
      int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len)
      +
      These functions copy len bytes from/to *reg_data starting at I2C address addr and register reg.

      void I2cScan(char *devs, unsigned int devs_len)
      +
      This functions writes a list of I2C addresses in use into the string *dev with maximum length devs_len.

      bool I2cDevice(byte addr)
      +
      This functions checks if the I2C address addr is in use.

      Useful pre-processor directives~

      PSTR("string")

      This pre-processor directive saves RAM by storing strings in flash instead of RAM.

      const char MyTextStaticVariable[] PROGMEM = "string";
      +
      This pre-processor directive saves RAM by storing strings in flash instead of RAM.

      You may then reference them directly (if the type matches the parameter required) or force it to 4 byte alignment by using the variable as FPSTR(MyTextStaticVariable)

      Keeping ESP8266 code compact~

      Below are various tips and tricks to keep ESP8266 code compact and save both Flash and Memory. Flash code is limited to 1024k but keep in mind that to allow OTA upgrade, you need Flash memory to contain two firmwares at the same time. To go beyond 512k, you typically use tasmota-minimal as an intermediate firmware. tasmota-minimal takes roughly 360k, so it's safe not to go uint32_t beyond 620k of Flash. Memory is even more limited: 80k. With Arduino Core and basic Tasmota, there are 25k-30k left of heap space. Heap memory is very precious, running out of memory will generally cause a crash.

      About ESP8266~

      ESP8266 is based on Xtensa instruction set. Xtensa is a 32 bits RISC processor core, containing 16 x 32 bits registers. ESP8266 supports integer operations, including 32x32 multiplication. It does not contain an FPU for floating point operations, nor integer divisions.

      Contrary to classical RISC processors, all instructions are 24 bits wide instead of 32 bits. To increase code compactness, some instructions have a 16 bits version used whenever possible by gcc.

      If you want to see what assembly is generated by gcc, in file platform.ini, at the section used to compile (ex: [core_2_6_1]) in section build_flags add:

      -save-temps=obj -fverbose-asm

      Gcc will store <file>.s in the same folder as the .o file, typically in .pioenvs/.

      First example~

      Let's take a basic function:

      uint32_t Example(uint32_t a, uint32_t b) {
      +  return a + b;
      +}
      +

      Below is the generated assembly. Function names are mangled using standard C++, i.e. their name derive from their arguments and return types:

      _Z7Examplejj:
      +    add.n   a2, a2, a3  #, a, b
      +    ret.n
      +

      As you can see, this is the simplest function we can think of. Register A2 holds the first argument and is used for return value. A3 holds the second argument.

      uint8_t or uint32_t ?~

      uint32_t Example(uint32_t a, uint32_t b) {
      +  uint8_t c = a + b;
      +  return c;
      +}
      +

      Assembly:

      _Z7Examplejj:
      +    add.n   a2, a2, a3  # tmp52, a, b
      +    extui   a2, a2, 0, 8    #, tmp52
      +    ret.n
      +

      Whenever gcc needs to convert from uin32_t to uint8_t, it uses an extra instruction extui <reg>, <reg>, 0, 8.

      Whenever you allocate uint8_t as a local variable, it will anyways allocate 32 bits on the stack.

      In conclusion you can easily use uint32_t in many places in the code. The main reason to force uint8_t are:

      • in structures, to save memory. This is the only place where uint8_t will take 1 byte and the compiler will try to pack as much as 4 uint8_t in 32 bits
      • when you want to ensure that the value can never exceed 255. Beware though that the compiler will just chunk the last 8 bits of a 32 bits value and will not report any overflow.

      Loops~

      Should you use uint8_t or uint32_t for loops?

      Let's try:

      uint32_t Example(uint32_t a, uint32_t b) {
      +  for (uint8_t i = 0; i < 10; i++) {
      +    a += b;
      +  }
      +  for (uint32_t j = 0; j < 10; j++) {
      +    a += b;
      +  }
      +  return a;
      +}
      +

      Assembly:

      _Z7Examplejj:
      +    movi.n  a3, 0   # ivtmp$7334,                     <- loop 1
      +.L2031:
      +    add.n   a2, a2, a3  # a, a, ivtmp$7334
      +    addi.n  a3, a3, 1   # ivtmp$7334, ivtmp$7334,
      +    bnei    a3, 10, .L2031  # ivtmp$7334,,
      +    movi.n  a3, 0   # j,                              <- loop 2
      +.L2033:
      +    add.n   a2, a2, a3  # a, a, j
      +    addi.n  a3, a3, 1   # j, j,
      +    bnei    a3, 10, .L2033  # j,,
      +    ret.n
      +

      As you can see here, both loops generate the same assembly for fixed size loops.

      Let's now see for variable size loops.

      uint32_t Example(uint32_t a, uint32_t b) {
      +  for (uint8_t i = 0; i < b; i++) {
      +    a += i;
      +  }
      +  for (uint32_t j = 0; j < b; j++) {
      +    a += j;
      +  }
      +  return a;
      +}
      +

      Assembly:

      _Z7Examplejj:
      +    movi.n  a4, 0   # i,                     <- loop 1
      +    j   .L2030  #
      +.L2031:
      +    add.n   a2, a2, a4  # a, a, i
      +    addi.n  a4, a4, 1   # tmp48, i,
      +    extui   a4, a4, 0, 8    # i, tmp48       <- extra 32 to 8 bits conversion
      +.L2030:
      +    bltu    a4, a3, .L2031  # i, b,
      +    movi.n  a4, 0   # j,                     <- loop 2
      +    j   .L2032  #
      +.L2033:
      +    add.n   a2, a2, a4  # a, a, j
      +    addi.n  a4, a4, 1   # j, j,
      +.L2032:
      +    bne a4, a3, .L2033  # j, b,
      +    ret.n
      +

      In the first loop, the register a4 needs to be converted from 32 bits to 8 bits in each iteration.

      Again, there is no definitive rule, but keep in mind that using uint8_t can sometimes increase code size compared to uint32_t.

      Floats, not doubles!~

      ESP8266 does not have a FPU (Floating Point Unit), all floating point operations are emulated in software and provided in libm.a. The linker removes any unused functions, so we need to limit the number of floating point function calls.

      Rule 1: use ints where you can, avoid floating point operations.

      Rule 2: if you really need floating point, always use float, never ever use double.

      Let's now see why.

      floatfits in 32 bits, with a mantissa of 20 bits, exponent of TODO. The mantissa is 20 bits wide, which provides enough precision for most of our needs.

      float is 32 bits wide and fits in a single register, whereas double is 64 bits and requires 2 registers.

      float Examplef(float a, float b) {
      +  return sinf(a) * (b + 0.4f) - 3.5f;
      +}
      +

      Assembly:

          .literal .LC1012, 0x3ecccccd      <- 0.4f
      +    .literal .LC1013, 0x40600000      <- 3.5f
      +_Z8Examplefff:
      +    addi    sp, sp, -16 #,,       <- reserve 16 bytes on stack
      +    s32i.n  a0, sp, 12  #,        <- save a0 (return address) on stack
      +    s32i.n  a12, sp, 8  #,        <- save a12 on stack, to free for local var
      +    s32i.n  a13, sp, 4  #,        <- save a13 on stack, to free for local var
      +    mov.n   a13, a3 # b, b            <- a3 holds 'b', save to a13
      +    call0   sinf    #                 <- calc sin of a2 (a)
      +    l32r    a3, .LC1012 #,        <- load 0.4f in a3
      +    mov.n   a12, a2 # D.171139,       <- save result 'sin(a)' to a12
      +    mov.n   a2, a13 #, b              <- move a13 (second arg: b) to a2
      +    call0   __addsf3    #         <- add floats a2 and a3, result to a2
      +    mov.n   a3, a2  # D.171139,       <- copy result to a3
      +    mov.n   a2, a12 #, D.171139       <- load a2 with a12: sin(a)
      +    call0   __mulsf3    #         <- multiply 'sin(a)*(b+0.4f)'
      +    l32r    a3, .LC1013 #,        <- load a3 with 3.5f
      +    call0   __subsf3    #         <- subtract
      +    l32i.n  a0, sp, 12  #,        <- restore a0 (return address)
      +    l32i.n  a12, sp, 8  #,        <- restore a12
      +    l32i.n  a13, sp, 4  #,        <- restore a13
      +    addi    sp, sp, 16  #,,       <- free stack
      +    ret.n                             <- return
      +

      Now with double:

      double Exampled(double a, double b) {
      +  return sin(a) * (b + 0.4) - 3.5;
      +}
      +

      Assembly:

          .literal .LC1014, 0x9999999a, 0x3fd99999     <- 0.4
      +    .literal .LC1015, 0x00000000, 0x400c0000     <- 3.5
      +_Z8Exampleddd:
      +    addi    sp, sp, -32 #,,
      +    s32i.n  a0, sp, 28  #,
      +    s32i.n  a12, sp, 24 #,
      +    s32i.n  a13, sp, 20 #,
      +    s32i.n  a14, sp, 16 #,
      +    s32i.n  a15, sp, 12 #,
      +    mov.n   a14, a4 #,
      +    mov.n   a15, a5 #,
      +    call0   sin #
      +    l32r    a4, .LC1014 #,
      +    l32r    a5, .LC1014+4   #,
      +    mov.n   a12, a2 #,
      +    mov.n   a13, a3 #,
      +    mov.n   a2, a14 #,
      +    mov.n   a3, a15 #,
      +    call0   __adddf3    #
      +    mov.n   a4, a2  #,
      +    mov.n   a5, a3  #,
      +    mov.n   a2, a12 #,
      +    mov.n   a3, a13 #,
      +    call0   __muldf3    #
      +    l32r    a4, .LC1015 #,
      +    l32r    a5, .LC1015+4   #,
      +    call0   __subdf3    #
      +    l32i.n  a0, sp, 28  #,
      +    l32i.n  a12, sp, 24 #,
      +    l32i.n  a13, sp, 20 #,
      +    l32i.n  a14, sp, 16 #,
      +    l32i.n  a15, sp, 12 #,
      +    addi    sp, sp, 32  #,,
      +    ret.n
      +

      As you can see the double needs to move many more registers around. Examplef (float) is 84 bytes, Exampled (double) is 119 bytes (+42% code size). Actually it's even worse, sin is larger than float version sinf.

      Also, never forget to explicitly tag literals as float: always put 1.5f and not 1.5. Let's see the impact:

      float Examplef2(float a, float b) {
      +  return sinf(a) * (b + 0.4) - 3.5;    // same as above with double literals
      +}
      +

      Assembly:

          .literal .LC1014, 0x9999999a, 0x3fd99999
      +    .literal .LC1015, 0x00000000, 0x400c0000
      +    .align  4
      +    .global _Z9Examplef2ff
      +    .type   _Z9Examplef2ff, @function
      +_Z9Examplef2ff:
      +    addi    sp, sp, -16 #,,
      +    s32i.n  a0, sp, 12  #,
      +    s32i.n  a12, sp, 8  #,
      +    s32i.n  a13, sp, 4  #,
      +    s32i.n  a14, sp, 0  #,
      +    mov.n   a14, a3 # b, b
      +    call0   sinf    #
      +    call0   __extendsfdf2   #        <- extend float to double
      +    mov.n   a12, a2 #,
      +    mov.n   a2, a14 #, b
      +    mov.n   a13, a3 #,
      +    call0   __extendsfdf2   #        <- extend float to double
      +    l32r    a4, .LC1014 #,
      +    l32r    a5, .LC1014+4   #,
      +    call0   __adddf3    #        <- add double
      +    mov.n   a4, a2  #,
      +    mov.n   a5, a3  #,
      +    mov.n   a2, a12 #,
      +    mov.n   a3, a13 #,
      +    call0   __muldf3    #        <- multiply double
      +    l32r    a4, .LC1015 #,
      +    l32r    a5, .LC1015+4   #,
      +    call0   __subdf3    #        <- subtract double
      +    call0   __truncdfsf2    #        <- truncate double to float
      +    l32i.n  a0, sp, 12  #,
      +    l32i.n  a12, sp, 8  #,
      +    l32i.n  a13, sp, 4  #,
      +    l32i.n  a14, sp, 0  #,
      +    addi    sp, sp, 16  #,,
      +    ret.n
      +

      The last example takes 143 bytes, which is even worse than the double version, because of conversions from float to double and back. Internally, if you don't force float literals, gcc will make all intermediate compute in double and convert to float in the end. This is usually what is wanted: compute with maximum precision and truncate at the last moment. But for ESP8266 we want the opposite: most compact code.

      String concatenation~

      Let's start with an easy example:

      void ExampleStringConcat(String &s) {
      +  s += "suffix";
      +}
      +

      Assembly (25 bytes):

      .LC1024:
      +    .string "suffix"
      +    .literal .LC1025, .LC1024
      +_Z19ExampleStringConcatR6String:
      +    l32r    a3, .LC1025 #,
      +    addi    sp, sp, -16 #,,
      +    s32i.n  a0, sp, 12  #,
      +    call0   _ZN6String6concatEPKc   #
      +    l32i.n  a0, sp, 12  #,
      +    addi    sp, sp, 16  #,,
      +    ret.n
      +

      If you need to add more complex strings, do not concatenate using native c++ concat:

      void ExampleStringConcat2(String &s, uint8_t a, uint8_t b) {
      +  s += "[" + String(a) + "," + String(b) + "]";
      +}
      +

      Assembly (122 bytes!):

      .LC231:
      +    .string ","
      +.LC1026:
      +    .string "["
      +.LC1029:
      +    .string "]"
      +    .literal .LC1027, .LC1026
      +    .literal .LC1028, .LC231
      +    .literal .LC1030, .LC1029
      +_Z20ExampleStringConcat2R6Stringhh:
      +    addi    sp, sp, -64 #,,
      +    s32i.n  a13, sp, 52 #,
      +    extui   a13, a3, 0, 8   # a, a
      +    l32r    a3, .LC1027 #,
      +    s32i.n  a12, sp, 56 #,
      +    mov.n   a12, a2 # s, s
      +    addi.n  a2, sp, 12  #,,
      +    s32i.n  a0, sp, 60  #,
      +    s32i.n  a14, sp, 48 #,
      +    extui   a14, a4, 0, 8   # b, b
      +    call0   _ZN6StringC2EPKc    # .    <- allocate String
      +    movi.n  a4, 0xa #,
      +    addi    a2, sp, 24  #,,
      +    mov.n   a3, a13 #, a
      +    call0   _ZN6StringC1Ehh #              <- allocate String
      +    addi    a3, sp, 24  #,,
      +    addi.n  a2, sp, 12  #,,
      +    call0   _ZplRK15StringSumHelperRK6String    #
      +    l32r    a3, .LC1028 #,
      +    call0   _ZplRK15StringSumHelperPKc  #
      +    movi.n  a4, 0xa #,
      +    mov.n   a13, a2 # D.171315,
      +    mov.n   a3, a14 #, b
      +    mov.n   a2, sp  #,
      +    call0   _ZN6StringC1Ehh #              <- allocate String
      +    mov.n   a3, sp  #,
      +    mov.n   a2, a13 #, D.171315
      +    call0   _ZplRK15StringSumHelperRK6String    #
      +    l32r    a3, .LC1030 #,
      +    call0   _ZplRK15StringSumHelperPKc  #
      +    mov.n   a3, a2  # D.171315,
      +    mov.n   a2, a12 #, s
      +    call0   _ZN6String6concatERKS_  #
      +    mov.n   a2, sp  #,
      +    call0   _ZN6StringD1Ev  #              <- destructor
      +    addi    a2, sp, 24  #,,
      +    call0   _ZN6StringD1Ev  #              <- destructor
      +    addi.n  a2, sp, 12  #,,
      +    call0   _ZN6StringD2Ev  #          <- destructor
      +    l32i.n  a0, sp, 60  #,
      +    l32i.n  a12, sp, 56 #,
      +    l32i.n  a13, sp, 52 #,
      +    l32i.n  a14, sp, 48 #,
      +    addi    sp, sp, 64  #,,
      +    ret.n
      +

      Instead use native String concat:

      void ExampleStringConcat3(String &s, uint8_t a, uint8_t b) {
      +  s += "[";
      +  s += a;
      +  s += ",";
      +  s += b;
      +  s += "]";
      +}
      +

      Assembly (69 bytes, -43%):

      .LC231:
      +    .string ","
      +.LC1026:
      +    .string "["
      +.LC1029:
      +    .string "]"
      +    .literal .LC1031, .LC1026
      +    .literal .LC1032, .LC231
      +    .literal .LC1033, .LC1029
      +_Z20ExampleStringConcat3R6Stringhh:
      +    addi    sp, sp, -16 #,,
      +    s32i.n  a13, sp, 4  #,
      +    extui   a13, a3, 0, 8   # a, a
      +    l32r    a3, .LC1031 #,
      +    s32i.n  a0, sp, 12  #,
      +    s32i.n  a12, sp, 8  #,
      +    s32i.n  a14, sp, 0  #,
      +    mov.n   a12, a2 # s, s
      +    extui   a14, a4, 0, 8   # b, b
      +    call0   _ZN6String6concatEPKc   #       <- native char* add
      +    mov.n   a3, a13 #, a
      +    mov.n   a2, a12 #, s
      +    call0   _ZN6String6concatEh #       <- native int add
      +    l32r    a3, .LC1032 #,
      +    mov.n   a2, a12 #, s
      +    call0   _ZN6String6concatEPKc   #       <- native char* add
      +    mov.n   a3, a14 #, b
      +    mov.n   a2, a12 #, s
      +    call0   _ZN6String6concatEh #       <- native int add
      +    l32r    a3, .LC1033 #,
      +    mov.n   a2, a12 #, s
      +    call0   _ZN6String6concatEPKc   #       <- native char* add
      +    l32i.n  a0, sp, 12  #,
      +    l32i.n  a12, sp, 8  #,
      +    l32i.n  a13, sp, 4  #,
      +    l32i.n  a14, sp, 0  #,
      +    addi    sp, sp, 16  #,,
      +    ret.n
      +

      Extension to (v)snprintf()~

      Tasmota code uses extensively snprintf() to build Web UI, MQTT messages and logs.

      However there are some limitations. First we use a stripped down version of (v)snprintf() to save a big amount of code size; and types like float or uint64 or not supported. Second Arduino core often uses high level objects like IPAddress that are not natively supported by snprintf().

      GCC libc normally includes a way to extend printf to new data types, but again the reduced lib in Arduino does not provide such a standard extension mechanism.

      We have now build an extension mechanism to snprintf() to allow for simpler code and less flash space.

      How to use~

      All extensions are using %_<x> where <x> is one of the following:

      Warning: most high-level values must be passed by address

      • %_X: support for uint64_t.

      Example:

      uint64_t u64 = 0x1122334455667788LL;
      +ext_snprintf(PSTR("Int64 = 0x%_X"), &u64);
      +
      • %_I: support for IPv4 ip address in the form of uint32_t. Note: it is passed by value, not by address.

      Example:

      uint32_t ip = 0x10203040;
      +ext_snprintf(PSTR("IP = 0x%_I"), ip);
      +
      • %_f or %*_f or %<number>_f: support for float. Note: float must be passed by address (otherwise it is silently converted to double and creates alignment issues on the stack).

      When using %*_f, the first argument specifies the number d of decimal for the float, as a signed int. d can also be directly coded in the format ex: %2_f of %-2_f.

      If d > 0 we output exactly d decimals (even zeros), if d < 0 we output d decimals but remove any trailing zeros. Default value is -2 (2 decimals).

      Example:

      char s[128];
      +float fl = 3.14;
      +ext_snprintf(s, sizeof(s), PSTR("f1=%*_f f2=%*_f f3=%4_f f4=%-4_f"), 4, &fl, -4, &fl, &fl, &fl);
      +// outputs:
      +// "f1=3.1400 f2=3.14 f3=3.1400 f4=3.14"
      +
      • %*_H: prints an array of bytes as Hex (uppercase). The first argument is the length in bytes of the array (if zero or negative, it outputs an empty string). The second argument is the pointer to the array of bytes. The pointer can be null if the length is zero or negative. The pointer can be in PROGMEM. Note: %_H will output an empty string because it is missing the length.

      Example:

      char s[16];
      +const uint8_t b[] PROGMEM = { 0x00, 0x01, 0x80, 0xFF };
      +ext_snprintf(s, sizeof(s), PSTR("Hex=%*_H"), sizeof(b), b);
      +// outputs:
      +// "Hex=000180FF"
      +
      • %_B: this is equivalent to %*_H but directly takes an SBuffer() object.

      Example:

      char s[16];
      +SBuffer b(8);    // statically allocate 8 bytes
      +b.add8(0x5A);
      +b.add8(0xA5);
      +
      +ext_snprintf(s, sizeof(s), PSTR("Hex=%_B"), &b);
      +// outputs:
      +// "Hex=5AA5"
      +

      Code size reduction~

      Floats~

      It is not needed to call explicitly dtostrfd() anymore:

      Before:

      int ResponseAppendTHD(float f_temperature, float f_humidity)
      +{
      +  char temperature[FLOATSZ];
      +  dtostrfd(f_temperature, Settings.flag2.temperature_resolution, temperature);
      +  char humidity[FLOATSZ];
      +  dtostrfd(f_humidity, Settings.flag2.humidity_resolution, humidity);
      +  char dewpoint[FLOATSZ];
      +  dtostrfd(CalcTempHumToDew(f_temperature, f_humidity), Settings.flag2.temperature_resolution, dewpoint);
      +
      +  return ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_DEWPOINT "\":%s"), temperature, humidity, dewpoint);
      +}
      +

      Assembly (117 bytes):

      _Z17ResponseAppendTHDff:
      +    addi    sp, sp, -64 #,,
      +    s32i.n  a0, sp, 60  #,
      +    s32i.n  a12, sp, 56 #,
      +    s32i.n  a13, sp, 52 #,
      +    s32i.n  a14, sp, 48 #,
      +    mov.n   a13, a3 # f_humidity, f_humidity
      +    mov.n   a14, a2 # f_temperature, f_temperature
      +    call0   __extendsfdf2   #
      +    l32r    a12, .LC658 #, tmp57
      +    addi    a5, sp, 32  #,,
      +    addmi   a12, a12, 0x500 # tmp60, tmp57,
      +    l32i    a4, a12, 188    # Settings, Settings
      +    extui   a4, a4, 30, 2   #, Settings,
      +    call0   _Z8dtostrfddhPc #
      +    mov.n   a2, a13 #, f_humidity
      +    call0   __extendsfdf2   #
      +    l32i    a4, a12, 188    # Settings, Settings
      +    addi    a5, sp, 16  #,,
      +    extui   a4, a4, 28, 2   #, Settings,,
      +    call0   _Z8dtostrfddhPc #
      +    mov.n   a3, a13 #, f_humidity
      +    mov.n   a2, a14 #, f_temperature
      +    call0   _Z16CalcTempHumToDewff  #
      +    call0   __extendsfdf2   #
      +    l32i    a4, a12, 188    # Settings, Settings
      +    mov.n   a5, sp  #,
      +    extui   a4, a4, 30, 2   #, Settings,
      +    call0   _Z8dtostrfddhPc #
      +    l32r    a2, .LC659  #,
      +    addi    a3, sp, 32  #,,
      +    addi    a4, sp, 16  #,,
      +    mov.n   a5, sp  #,
      +    call0   _Z16ResponseAppend_PPKcz    #
      +    l32i.n  a0, sp, 60  #,
      +    l32i.n  a12, sp, 56 #,
      +    l32i.n  a13, sp, 52 #,
      +    l32i.n  a14, sp, 48 #,
      +    addi    sp, sp, 64  #,,
      +    ret.n
      +

      After:

      int ResponseAppendTHD(float f_temperature, float f_humidity)
      +{
      +  float dewpoint = CalcTempHumToDew(f_temperature, f_humidity);
      +
      +  return ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%*_f,\"" D_JSON_HUMIDITY "\":%*_f,\"" D_JSON_DEWPOINT "\":%*_f"),
      +                          Settings.flag2.temperature_resolution, &f_temperature,
      +                          Settings.flag2.humidity_resolution, &f_humidity,
      +                          Settings.flag2.temperature_resolution, &dewpoint);
      +}
      +

      Assembly (61 bytes):

      _Z17ResponseAppendTHDff:
      +    addi    sp, sp, -64 #,,
      +    s32i.n  a0, sp, 60  #,
      +    s32i.n  a3, sp, 36  # f_humidity, f_humidity
      +    s32i.n  a2, sp, 32  # f_temperature, f_temperature
      +    call0   _Z16CalcTempHumToDewff  #
      +    s32i.n  a2, sp, 16  # dewpoint,
      +    l32r    a2, .LC658  #, tmp51
      +    addi    a4, sp, 32  #,,
      +    addmi   a2, a2, 0x500   # tmp54, tmp51,
      +    l32i    a5, a2, 188 # Settings, Settings
      +    addi    a2, sp, 16  # tmp68,,
      +    extui   a7, a5, 30, 2   # D.156427, Settings,
      +    s32i.n  a2, sp, 0   #, tmp68
      +    l32r    a2, .LC659  #,
      +    addi    a6, sp, 36  #,,
      +    mov.n   a3, a7  #, D.156427
      +    extui   a5, a5, 28, 2   #, Settings,,
      +    call0   _Z16ResponseAppend_PPKcz    #
      +    l32i.n  a0, sp, 60  #,
      +    addi    sp, sp, 64  #,,
      +    ret.n
      +
      \ No newline at end of file diff --git a/Sensor-Configuration/index.html b/Sensor-Configuration/index.html new file mode 100644 index 0000000000..b8e2cb16af --- /dev/null +++ b/Sensor-Configuration/index.html @@ -0,0 +1,11 @@ + + + + + + + + +Redirecting... + + diff --git a/Serial-to-TCP-Bridge/index.html b/Serial-to-TCP-Bridge/index.html new file mode 100644 index 0000000000..4efaacbcb4 --- /dev/null +++ b/Serial-to-TCP-Bridge/index.html @@ -0,0 +1,4 @@ + Serial to TCP Bridge - Tasmota
      Skip to content

      Serial to TCP Bridge~

      This feature is included only in tasmota-zbbridge and tasmota-zbbrdgpro binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_TCP_BRIDGE 
      +#define USE_TCP_BRIDGE          //  Add support for Serial to TCP bridge (+1.3k code)
      +#endif
      +

      Connect to a serial device over the network, also known as ser2net

      This feature can be used to add a "serial to network" functionality to a device that is otherwise serial only. You connect the device to a ESP8266/ESP32 and Tasmota will bridge between serial and network.

      This is commonly used with a CCxxxx Zigbee based module to connect it to a remote ZHA or zigbee2mqtt instance.

      Commands~

      • TCPBaudRate <x>: sets the baud rate for serial (only 8N1 mode), min 1200, max 115200 by 1200 increments.
      • TCPStart <port>: listens to port <port>. This features supports 2 parallel TCP connections, which can be useful if you need a terminal + a specific protocol (like XMODEM). The 3rd connection will disconnect a previous connection. The number of parallel connections is a compile-time option. Note that this can be accessed by any host on the network and may have security implications.
      • TCPStart <port>,<ip address>: listens to port <port>, but only allows connections from the provided IPv4 address. Any connections from a different host will be immediately closed.
      • TCPStart 0 or TCPStart: shuts down the TCP server and disconnects any existing connection(s).

      For security reasons, the TCP bridge is not started at boot, and requires an explicit TCPStart command (this can be automated with Rules).

      Configuration~

      First assign two GPIOs to TCP Tx (208) and TCP Rx (209) types in the "Configure Module" page. The Rx/Tx are relative to the ESP device. For example with ESP01's hardware serial, set GPIO1 as TCP Tx and GPIO3 as TCP Rx.

      Then set baud rate with TCPBaudRate and port with TCPStart.

      You can add a rule to start the TCP server at boot.
      To do this for port 8888 and allow connections only from host 192.168.0.10, run Rule1 ON System#Boot do TCPStart 8888,192.168.0.10 endon then enable with Rule1 1 and restart the device.

      Additional resources~

      PR
      Sonoff ZBBridge reference

      \ No newline at end of file diff --git a/SetOption36/index.html b/SetOption36/index.html new file mode 100644 index 0000000000..eb8e873013 --- /dev/null +++ b/SetOption36/index.html @@ -0,0 +1 @@ + SetOption36 - Tasmota

      SetOption36

      SetOption36 is deprecated in favour of using the sleep command to use either Dynamic or Normal sleep as from Tasmota version 6.3.0.15.

      Click here for Dynamic Sleep

      \ No newline at end of file diff --git a/SetOption37/index.html b/SetOption37/index.html new file mode 100644 index 0000000000..0c12036129 --- /dev/null +++ b/SetOption37/index.html @@ -0,0 +1 @@ + SetOption37 - Tasmota

      SetOption37

      SetOption37 is used for re-mapping the RGBWcWw channels. This transformation happens right before the hardware-specific output, so all the advanced features (schemes, hsb setting, etc.) works with the original, un-mapped values.

      The main reason for this option is to provide support for bulbs whose physical wiring is almost the usual/standard one, but the channels are arranged in a different way.

      For technical reasons this transformation isn't a full N-to-N mapping, it can only the re-arrange the channels, which is equivalent to map the (R,G,B,Wc,Ww) channels to one of their permutations.

      The required permutation can be configured via SetOption37 n, where n is the systematic index of the permutation, a number from the [0 .. 119] interval, from 0=(R,G,B,Wc,Ww) to 119=(Ww,Wc,B,G,R):

      Idx Perm. Idx Perm. Idx Perm. Idx Perm.
      0 R,G,B,Wc,Ww 1 R,G,B,Ww,Wc 2 R,G,Wc,B,Ww 3 R,G,Wc,Ww,B
      4 R,G,Ww,B,Wc 5 R,G,Ww,Wc,B 6 R,B,G,Wc,Ww 7 R,B,G,Ww,Wc
      8 R,B,Wc,G,Ww 9 R,B,Wc,Ww,G 10 R,B,Ww,G,Wc 11 R,B,Ww,Wc,G
      12 R,Wc,G,B,Ww 13 R,Wc,G,Ww,B 14 R,Wc,B,G,Ww 15 R,Wc,B,Ww,G
      16 R,Wc,Ww,G,B 17 R,Wc,Ww,B,G 18 R,Ww,G,B,Wc 19 R,Ww,G,Wc,B
      20 R,Ww,B,G,Wc 21 R,Ww,B,Wc,G 22 R,Ww,Wc,G,B 23 R,Ww,Wc,B,G
      24 G,R,B,Wc,Ww 25 G,R,B,Ww,Wc 26 G,R,Wc,B,Ww 27 G,R,Wc,Ww,B
      28 G,R,Ww,B,Wc 29 G,R,Ww,Wc,B 30 G,B,R,Wc,Ww 31 G,B,R,Ww,Wc
      32 G,B,Wc,R,Ww 33 G,B,Wc,Ww,R 34 G,B,Ww,R,Wc 35 G,B,Ww,Wc,R
      36 G,Wc,R,B,Ww 37 G,Wc,R,Ww,B 38 G,Wc,B,R,Ww 39 G,Wc,B,Ww,R
      40 G,Wc,Ww,R,B 41 G,Wc,Ww,B,R 42 G,Ww,R,B,Wc 43 G,Ww,R,Wc,B
      44 G,Ww,B,R,Wc 45 G,Ww,B,Wc,R 46 G,Ww,Wc,R,B 47 G,Ww,Wc,B,R
      48 B,R,G,Wc,Ww 49 B,R,G,Ww,Wc 50 B,R,Wc,G,Ww 51 B,R,Wc,Ww,G
      52 B,R,Ww,G,Wc 53 B,R,Ww,Wc,G 54 B,G,R,Wc,Ww 55 B,G,R,Ww,Wc
      56 B,G,Wc,R,Ww 57 B,G,Wc,Ww,R 58 B,G,Ww,R,Wc 59 B,G,Ww,Wc,R
      60 B,Wc,R,G,Ww 61 B,Wc,R,Ww,G 62 B,Wc,G,R,Ww 63 B,Wc,G,Ww,R
      64 B,Wc,Ww,R,G 65 B,Wc,Ww,G,R 66 B,Ww,R,G,Wc 67 B,Ww,R,Wc,G
      68 B,Ww,G,R,Wc 69 B,Ww,G,Wc,R 70 B,Ww,Wc,R,G 71 B,Ww,Wc,G,R
      72 Wc,R,G,B,Ww 73 Wc,R,G,Ww,B 74 Wc,R,B,G,Ww 75 Wc,R,B,Ww,G
      76 Wc,R,Ww,G,B 77 Wc,R,Ww,B,G 78 Wc,G,R,B,Ww 79 Wc,G,R,Ww,B
      80 Wc,G,B,R,Ww 81 Wc,G,B,Ww,R 82 Wc,G,Ww,R,B 83 Wc,G,Ww,B,R
      84 Wc,B,R,G,Ww 85 Wc,B,R,Ww,G 86 Wc,B,G,R,Ww 87 Wc,B,G,Ww,R
      88 Wc,B,Ww,R,G 89 Wc,B,Ww,G,R 90 Wc,Ww,R,G,B 91 Wc,Ww,R,B,G
      92 Wc,Ww,G,R,B 93 Wc,Ww,G,B,R 94 Wc,Ww,B,R,G 95 Wc,Ww,B,G,R
      96 Ww,R,G,B,Wc 97 Ww,R,G,Wc,B 98 Ww,R,B,G,Wc 99 Ww,R,B,Wc,G
      100 Ww,R,Wc,G,B 101 Ww,R,Wc,B,G 102 Ww,G,R,B,Wc 103 Ww,G,R,Wc,B
      104 Ww,G,B,R,Wc 105 Ww,G,B,Wc,R 106 Ww,G,Wc,R,B 107 Ww,G,Wc,B,R
      108 Ww,B,R,G,Wc 109 Ww,B,R,Wc,G 110 Ww,B,G,R,Wc 111 Ww,B,G,Wc,R
      112 Ww,B,Wc,R,G 113 Ww,B,Wc,G,R 114 Ww,Wc,R,G,B 115 Ww,Wc,R,B,G
      116 Ww,Wc,G,R,B 117 Ww,Wc,G,B,R 118 Ww,Wc,B,R,G 119 Ww,Wc,B,G,R

      Source: this spreadsheet

      \ No newline at end of file diff --git a/Shutter-and-Steppers/index.html b/Shutter-and-Steppers/index.html new file mode 100644 index 0000000000..de3ef86a7a --- /dev/null +++ b/Shutter-and-Steppers/index.html @@ -0,0 +1 @@ + Shutter and Steppers - Tasmota
      \ No newline at end of file diff --git a/Smart-Meter-Interface/index.html b/Smart-Meter-Interface/index.html new file mode 100644 index 0000000000..cccc0efd99 --- /dev/null +++ b/Smart-Meter-Interface/index.html @@ -0,0 +1,2191 @@ + Smart Meter Interface - Tasmota
      Skip to content

      Smart Meter Interface~

      This driver extracts selected values from Smart Meters over various protocols, filters and publishes them to MQTT as regular sensors.

      This feature is not included in precompiled binaries

      Based on Tasmota's scripting language. To use it you must compile your build. Add the following to user_config_override.h:

      #ifndef USE_SCRIPT
      +#define USE_SCRIPT
      +#endif
      +#ifndef USE_SML_M
      +#define USE_SML_M
      +#endif
      +#ifdef USE_RULES
      +#undef USE_RULES
      +#endif
      +

      Additional features can be enabled by adding the following #define compiler directive parameters and then compiling the firmware. These parameters are explained further below in the article. Most features are now enabled by default, others may be set in the descriptor per meter.

      Feature Description
      SML_BSIZ n (default 48) Maximum number of characters per line in serial input buffer. Complete chunk of serial data must fit into this size, so include any CR/LF if that applies. can now be defined per meter in descriptor, see special options
      MAX_METERS n (default 5) Maximum number of meters. Decrease this to 1 for example if you have a meter with many lines and lots of characters per descriptor line.
      TMSBSIZ n (default 256) Maximum number of characters in serial IRQ buffer (should always be larger than SML_BSIZ and even larger on high baud rates).can now be defined per meter in descriptor, see special options
      SML_DUMP_SIZE n (default 128) Maximum number of characters per line in dump mode. Only use if you have long strings comin in and they truncate.
      SML_PREFIX_SIZE n (default 8) Maximum number of characters + 1 for SML jsonPrefix.
      USE_ESP32_SW_SERIAL enables additional software serial channels for ESP32, (receive only), define pin with '-' sign to assign to software serial
      NO_USE_SML_SPECOPT disables special decoder entry to specify direction bit for some SML meters
      NO_USE_SML_SCRIPT_CMD disables some special SML script cmds and allows access to sml vars in other parts of the script. Is needed by some of the examples below.
      NO_SML_REPLACE_VARS disables replacement of any text in descriptor by script text variables. Useful if several occurrences of a text occupies a lot of space and you get short of script buffer. Readability may get worse so only makes sense on large descriptors. Note: to use % symbol un measurement units, you need to escape it like %%.
      NO_USE_SML_DECRYPT disables decoding of encrypted ams meters. decrypting needs TLS, so must define USE_TLS also.
      USE_SML_AUTHKEY enables authentication, this is not needed by most energy meters.
      USE_SML_TCP enables TCP MODBUS support.

      General description~

      To use this interface, connect the meter to available GPIO pins. These GPIOs must be set as None in Tasmota. If the interface detects that a script driven meter descriptor GPIO conflicts with a Tasmota GPIO setting, the interface will generate a duplicate GPIO defined error in the log and the meter descriptor will be ignored.

      Note

      When changing GPIO configurations, especially in conjunction with other Tasmota drivers, a restart may be required.

      Note

      On an ESP32, due to a different implementation, serial ports may not be used in conjunction with other Tasmota serial devices.

      Note

      when using bidirectional serial IO (receive and transmit), hardware serial is recommended.

      The Smart Meter Interface provides a means to connect many kinds of meters to Tasmota. The following types of meter protocols are supported:

      Protocol Description
      OBIS ASCII telegrams emitted from many smart meters, including P1 Smart Meters
      OBIS Binary SML telegrams emitted from many smart meters
      MODBus Binary telegrams used by many power meters and industrial devices
      Kamstrup Binary telegrams used by many power meters from Kamstrup
      EBus Binary telegrams emitted by many heaters and heat pumps (e.g. Vaillant, Wolf)
      VBus Binary telegrams emitted by many solar thermal systems boilers (e.g. Resol, Viessmann)
      RAW Binary decodes all kinds of binary data eg EMS heater bus
      Counter interface uses Tasmota counter storage (for e.g. REED contacts either in polling or IRQ mode)

      There are many different meters that use the same protocol. There are multitudes of variants and use cases. This interface provides a means of specifying these definitions through meter descriptors. This method uses the scripting language editor to define the descriptors. In this way, only one firmware binary version is required and a modification can be made easily on the fly.

      Note

      Additional hardware may be required to read certain measuring devices. For example: RS485toTTL adapter for Modbus, IR transistor for electricity meters. Sometimes an additional IR diode and resistors.

      By default, a SENSOR telegram is sent to MQTT every 300 seconds. This can be adjusted by changing the TelePeriod.

      Descriptor Syntax~

      This section must be present, even if it's empty. If compiled with SML_REPLACE_VARS, here is the place where text variables can be defined for the script:

      >D


      Declare >B (boot) section to inform the interface to read the meter descriptor(s):

      >B
      =>sensor53 r


      (Optional) declare >S section with additional scripting commands:

      >S <n>


      Declare >M section with the number of connected meters (n = 1..5):

      >M <n>

      Note

      If no >M section is found in the script or if the scripting language is not compiled, the driver reverts to the default hardcoded #define definitions. If no meter script is defined, the default hardcoded descriptor interface (deprecated) uses RX GPIO3 for receiving data and thus may interfere with other Tasmota Definitions without warning.

      Note

      Software serial only supports 8N1 serial format. Must use hardware serial for e.g. 8E1!

      Meter Definition~

      +<M>,<rxGPIO>,<type>,<flag>,<parameter>,<jsonPrefix>{,<txGPIO>,<txPeriod>,<cmdTelegram>}

      Parameter Description
      +<M> Meter number. The number must be increased with each additional Meter (default 1 to 5).
      <rxGPIO> The GPIO pin number where meter data is received.
      [xxx.xxx.xxx.xxx] IP number instead of pin number enables MODBUS TCP mode, the tcp port number is given at the baudrate position. (tx pin can be any number and is ignored)
      <type> The type of meter:
      - o - OBIS ASCII type of coding
      - s - SML binary smart message coding
      - e - EBus binary coding
      - v - VBus binary coding
      - m - MODBus binary coding with serial mode 8N1
      - M - MODBus binary coding with serial mode 8E1
      - k - Kamstrup binary coding with serial mode 8N1
      - c - Counter type
      - r - Raw binary coding (any binary telegram)
      <flag> Options flag:
      - 0 - counter without pullup
      - 1 - counter with pullup
      - 16 - enable median filter for that meter. Can help with sporadic dropouts, reading errors (not available for counters). this option is enabled by default #define USE_SML_MEDIAN_FILTER, if you are low on memory and dont use this feature you may outcomment this define in the driver
      <parameter> Parameters according to meter type:
      - for o,s,e,v,m,M,k,r types: serial baud rate e.g. 9600 (or port# for Modbus TCP).
      - for c type: a positive value = counter poll interval (not really recommended) or a negative value = debounce time (milliseconds) for irq driven counters.
      <jsonPrefix> Prefix for Web UI and MQTT JSON payload. Up to 7 characters.
      <txGPIO> The GPIO pin number where meter command is transmitted (optional).
      <tx enable> The GPIO pin number to enable transmitter (RS485) may follow the TX pin in bracket (pin) without a colon an 'i' in front of the pin number means 'inverted' (optional).
      <txPeriod> Period to repeat the transmission of commands to the meter (optional). Number of 100ms increments (n * 100ms).
      <cmdTelegram> Comma separated hex coded byte blocks to send to meter device. For MODBus each comma separated block is a command to retrieve a certain register from the meter (optional: only required for measuring devices that have to be triggered with a certain character string).

      Note

      for other serial protocols you may specify the exact mode (only for hardware serial) by the following code after the type specifier:

      N =no parity
      E =even parity
      O =odd parity
      and number of stop-bits

      e.g for Modbus: mN1,mN2,mE1,mE2,mO1,mO2

      Example

      +1,3,o,0,9600,OBIS1,1,2,2F3F210D0A
      ++1,3,o,0,9600,OBIS1,1(i4),2,2F3F210D0A  with pin 4 as inverted TX enable
      ++1,3,o,16,115200,NormalTariff,1
      ++1,3,s,16,9600,SML1
      ++1,12,c,1,-10,H20_Cnt
      ++1,3,v,0,9600,Solar
      +

      Example

      For MODBus: +1,3,m,0,9600,MODBUS,1,1,01040000,01040002,01040004,01040006,01040008,0104000a,0104000c,0104000e,01040010
      Components of the character string:
      ...01040000,01040002,...
      01 = Modbus slave device ID
      04 = Instruction to read an Input Register (alternatively, 03 = Instruction to read a Holding Register)
      0000/0002 = Register # (as Hexadecimal codification, without the prefix 0x. Example: 0x0079 -> 0079)
      the number of requested registers is fixed to 2, however with the char 'r' before the hex string the complete request string may be specified
      ...r010400000001,r010400020003,...
      Note: ID, Instruction to read the register value (Input vs Holding) and Register# may differ depending on the measuring device.

      Meter Metrics~

      Each meter typically provides multiple metrics (energy, voltage, power, current etc.) which it measures. An entry for each metric to be collected must be specified. An entry defines how to decode the data and put it into variables.

      <M>,<decoder>@<scale><offs>,<label>,<UoM>,<var>,<precision>

      Parameter Description
      <M> The meter number to which this decoder belongs
      <decoder> Decoding specification: OBIS as ASCII; SML, EBus, VBus, MODBus, RAW as HEX ASCII etc. No space characters allowed in this section!
      OBIS: ASCII OBIS code terminated with ( character which indicates the start of the meter value
      Counter: ASCII code 1-0:1.8.0*255 for counter value, code 1-0:1.7.0*255 for pulse rate (e.g. for actual power value)
      SML: SML binary OBIS as hex terminated with 0xFF indicating start of SML encoded value
      EBus, MODBus, RAW - hex values of data blocks to compare:
      - xx = ignore value (1 byte) or xN = ignore N bytes
      - ss = extract a signed byte
      - uu = extract an unsigned byte
      - UUuu = extract an unsigned word (high order byte first)
      - uuUU = extract an unsigned word (low order byte first)
      - UUuuUUuu or U32 = extract an unsigned long word (high order byte first)
      - uuUUuuUU or u32 = extract an unsigned long word (low order byte first)
      - SSss = extract a signed word (high order byte first)
      - ssSS = extract a signed word (low order byte first)
      - SSssSSss or S32 = extract a signed long word (high order byte first)
      - ssSSssSS or s32 = extract a signed long word (low order byte first)
      - on long word values, if a trailing s is added at the end of the mask, word order is reversed
      - U64 = extract an unsigned 64 long word
      - u64 = extract an unsigned 64 long word (low order byte first)
      - bcdN = extract a binary coded decimal N=2..12
      - ffffffff = extract a float value - IEEE754 decode
      - FFffFFff = extract a reverse float value - IEEE754 decode
      - kstr = decode KAMSTRUP data
      - pm(x.y.z) = pattern match(asci obis code)
      - pm(hHHHHHH) = pattern match(hex obis code)
      - pm(rHHHHHH) = pattern match(any hex pattern)

      if using VBus - hex values of data blocks to compare:
      - AAffffaddrff0001ffff = VBus-specific hex header: AA-sync byte, addr-the reversed address of the device. To find his out first look up the known hex address of the device. E.g. Resol DeltaSol BS Plus is 0x4221. Reverse it (without 0x) and you will get 21 42 hex characters. Now turn on raw dump mode using command sensor53 d1 and look for rows starting with aa, containing your reversed address at position 4 and 5 and 00 01 hex characters at position 7 and 8. If found, the entire header will be 10 hex characters long including aa (20 ascii chars without space, e.g. for Resol DeltaSol BS Plus this will be AA100021421000010774). At position 9 you see the number of frames containing readable data. To turn off raw dump use sensor53 d0.
      - v = VBus protocol indicator
      - oN = extract data from offset N (see offsets of your device in VBus protocol documentation)
      - u or s = extract unsigned or signed data
      - w or b = extract word or byte
      End of decoding: @ indicates termination of the decoding procedure.
      - ( following the @ character in case of obis decoder indicates to fetch the 2. value in brackets, not the 1. value. (e.g. to get the second value from an obis like 0-1:24.2.3(210117125004W)(01524.450*m3))
      - decoding multiple values coming in brackets after each other is possible with (@(0:1, (@(1:1, (@(2:1 and so on (e.g. to get values from an obis like 0-0:98.1.0(210201000000W)(000000.000*kWh)(000000.000*kWh))
      - decoding a 0/1 bit is indicated by a @ character followed by bx: (x = 0..7) extracting the corresponding bit from a byte. (e.g.: 1,xxxx5017xxuu@b0:1,Solarpump,,Solarpump,0)
      - in case of MODBus/Kamstrup, ix: designates the index (x = 0..n) referring to the requested block in the transmit section of the meter definition
      <scale> scaling factor (divisor) or string definition
      This can be a fraction (e.g., 0.1 = result * 10), or a negative value. When decoding a string result (e.g. meter serial number), use # character for this parameter (Note: only one string can be decoded per meter!). For OBIS, you need a ) termination character after the # character.
      <offs> optional offset must precede with + or - sign, note: offset is applied before scale!
      <label> web UI label (max. 23 characters)
      <UoM> unit of measurement (max. 7 characters)
      <var> MQTT label (max. 23 characters)
      <precision> number of decimal places. Add 16 to transmit the data immediately. Otherwise it is transmitted on TelePeriod only.

      Use ; character to comment lines in the script.

      Put # character at the end to terminate M section of the script.

      Example

      (OBIS/SML/MODBus):

      1,1-0:1.8.1*255(@1,Total consumption,KWh,Total_in,4`  
      +1,77070100010801ff@1000,W1,kWh,w1,4`  
      +1,010304UUuuxxxxxxxx@i0:1,Spannung L1,V,Voltage_L1,0`  
      +1,0:98.1.0(@(0:1,Havi adat, KWh,havi1,3`
      +1,0:98.1.0(@(1:1,Havi adat, KWh,havi2,3`
      +1,0:98.1.0(@(2:1,Havi adat, KWh,havi3,3`
      +

      OBIS: 1,1-0:0.0.0*255(@#),Meter Nr,, Meter_number,0

      Counter: 1,1-0:1.8.0*255(@1000,consumption,KWh,Total_in,3) precision of 3, scale for 1000 pulses/kWh
      1,1-0:1.7.0*255(@0.01667, power,W,Power_actual,0) actual power from pulse rate (in pulses/min) of counter meter, scale for 1 pulse/Wh (1 pulse/min => 60W; 1/60(=0.01667) (pulses/min)/W)

      SML: 1,77078181c78203ff@#,Service ID,,Meter_id,0
      1,1-0:1.8.0*255(@1,consumption,KWh,Total_in,4 precision of 4, transmitted only on TelePeriod
      1,1-0:1.8.0*255(@1,consumption,KWh,Total_in,20 precision of 4, transmitted immediately (4 + 16 = 20)

      MODBus: +1,3,M,1,9600,SBC,1,2,01030023,01030028...
      1,010304UUuuxxxxxxxx@i0:1,Voltage L1,V,Voltage_L1,0 the i0:1 refers to: 01030023 with a scaling factor (:1) for 1
      1,010304UUuuxxxxxxxx@i1:10,Current L1,V,Current_L1,2 the i1:10 refers to: 01030028 with a scaling factor (:10) for 10

      Tip

      Use: sensor53 dM to output the received data in the console. M = the number of the meter in the definition line.
      During the output of the data in the console, the data in the WEB UI are not updated. To return write: sensor53 d0

      Tip

      You can monitor the serial activity at a GPIO with a connected LED. This function is useful for debugging purposes and also to see data is coming in during normal operation.
      Usage: Enter the command sensor53 lx to activate this function (Lowercase L for LED). x is the number of the GPIO where the LED is connected. For example you can use sensor53 l2 for the onboard LED on a Wemos D1-mini or sensor53 l13 on a Sonoff Basic. sensor53 l255 turns the function off. This is the default value.
      With sensor53 mx you can choose which serial meter (x) will be monitored. Set sensor53 m0 will monitor all serial meters. This is the default value.
      To start the monitoring at boot-time, simply add the necessary entries in the boot-section (>B) of the script:

      Example

      >B
      +=>sensor53 r
      +=>sensor53 l13
      +

      Special Commands~

      With = character at the beginning of a line you can do some special decoding. With * character fields can be hidden or skipped.

      Command Description
      M,=m Perform arithmetic (+,-,*,/) on the measured data. Use # before a number to designate a constant value
      e.g. 1,=m 3+4+5/#3 @100,Voltage L1+L2+L3/3,V,Volt_avg,2 to sum results of decoder entries 3,4,5 and divide by 3 (average)
      M,=d Calculate difference between metric values decoded at time intervals (up to 10 =d lines possible)
      e.g. 1,=d 3 10 calculate 10 second interval difference of decoder entry 3
      M,=h Insert text on the web interface (html text up to 30 chars). These lines do not count as decoder entry.
      e.g. 1,=h<hr/> to insert a separator line on the web UI
      * character To hide fields from result output or disable output completely. Compiling with USE_SML_SCRIPT_CMD required.
      - as single character in <label> of the metrics line will hide that value from the web UI
      - as single character in <label> of the meter definition line will suppress the entire JSON output on MQTT
      M,=so1 special SML option for meters that use a bit in the status register to sign import or export like ED300L, AS2020 or DTZ541
      e.g. 1,=so1,00010800,65,11,65,11,00100700 for DTZ541
      1. obis code that holds the direction bit, 2. Flag identifier, 3. direction bit, 4. second Flag identifier (some meters use 2 different flags), 5. second bit, 6 obis code of value to be inverted on direction bit.
      M,=so2 if 1 fixes the bug introduced by meter DWS74, if 2 enabled OBIS line compare mode instead of shift compare mode, if 4 invert hardware serial line.
      e.g. 1,=so2,2 enable obis line compare.
      M,=so3 sets serial buffer size, serial IRQ buffer size and serial dump buffer size.
      enter as a new descriptor line e.g. 1,=so3,512 sets serial buffer size to 512. (default buffer is 48 bytes input, 128 bytes dump)
      M,=so4 sets AES decrytion key for encrypted meters.must define exactly 16 hexadecimal chars
      e.g. 1,=so4,deabcd0020a0cfdedeabcd0020a0cfde sets decryption key and enables decrypt mode for that meter.
      M,=so5 sets AES authentication key for encrypted meters.must define exactly 16 hexadecimal chars
      e.g. not needed by most energy meters (needs USE_SML_AUTHKEY).
      M,=so6 sync time in milliseconds for serial block detection with AMS meters (defaults to 1000).
      M,=so7 on ESP32 force selection of UART Nr. X (0,1,2) allows coexistence with other serial drivers

      Example

      To get the value of one of the descriptor lines, use sml[X]. X = Line number. Starts with 1. (compiling with USE_SML_SCRIPT_CMD required)

      >D
      +v1=0
      +v2=0
      +>S
      +;Writes the value of Descriptorline 1 to v1
      +v1=sml[1] 
      +;Writes the value of Descriptorline 2 to v2
      +v2=sml[2]
      +

      Example

      To disable and enable publishing of MQTT data on TelePeriod, use smlj=0 and smlj|=1, respectively. For example to skip first MQTT publishing after boot (may contain erroneous data at after restart if meter is slow, see Sanxing SX6x1):

      >B
      +;disable publishing at MQTT teleperiod, on boot
      +smlj=0
      +>S
      +;re-enable publishing at MQTT teleperiod, after 10 seconds of uptime
      +if upsecs>10
      +then
      +smlj|=1
      +endif
      +
      the variable smlj also switches to obis_line_mode if bit 1 = 1, default is 0

      Example

      If you have large meter descriptors and want to extract multiple values from the same descriptor, you can save flash space using SML_REPLACE_VARS at compile time (see Resol Deltasol BS Plus):

      >D
      +;define a text variable
      +r="1,AA100021421000010774"
      +>M 1
      +;in your meter definitions you can use your variable for the same descriptor
      ++1,3,v,0,9600,Solar
      +%r%vo12ut@#,time,,zeit,1
      +%r%vo0uw@10,S1 COL,°C,sens1,1
      +%r%vo2uw@10,S2 TST1,°C,sens2,1
      +%r%vo4uw@10,S3 TST2,°C,sens3,1
      +%r%vo6uw@10,S4 TR,°C,sens4,1
      +;%r% inserts the text variable and saves script storage space (3 instead of 22 chars for each line)
      +

      Attention

      With a few meters, it is necessary to request the meter to send data using a specific character string. This string has to be sent at a very low baud rate (300Baud). If you reply the meter with an acknowledge and ask the it for a new baud rate of 9600 baud, the baud rate of the SML driver has to be changed, too (see Landis + Gyr ZMR120AR).

      To change the baud rate:

      sml(METERNUMBER 0 BAUDRATE)

      For sending a specific character string:

      sml(METERNUMBER 1 STRING)

      And it works like this:

      >D
      +res=0  
      +scnt=0    
      +;For this Example in the >F section  
      +>F
      +;count 100ms   
      +scnt+=1  
      +switch scnt  
      +case 6  
      +;set sml driver to 300 baud and send /?! as HEX to trigger the Meter   
      +res=sml(1 0 300)  
      +res=sml(1 1 "2F3F210D0A")  
      +;1800ms later \> Send ACK and ask for switching to 9600 baud  
      +case 18  
      +res=sml(1 1 "063035300D0A")  
      +;2000ms later \> Switching sml driver to 9600 baud    
      +case 20  
      +res=sml(1 0 9600)   
      +;Restart sequence after 50x100ms    
      +case 50  
      +; 5000ms later \> restart sequence    
      +scnt=0  
      +ends        
      +>M 1
      ++1,3,o,0,9600, ,1  
      +;...etc.  
      +

      Note: This procedure is only necessary, if the meter explicitly asks for 300 baud. The most meters work directly with 9600 baud. Therefore it is easier to give this method a try (see Iskra MT 174):

      Meter#,GPIO# Input,TYPE,FLAG,Baudrate,JSONNAME,GPIO# Output,TX Period,Character string
      +1,3,o,0,9600,energy,1,4,2F3F210D0A

      Tip

      If you use a Wemos D1 Mini you could compile a 4M flash image with filesystem support so your script will survive upgrades and factory resets. To do this, create a new entry in platformio_tasmota_env.ini:

      [env:tasmota-4mb]
      +board_build.ldscript    = eagle.flash.4m2m.ld
      +
      Add tasmota-4mb to platformio.ini's build variants.

      Add the following to user_config_override.h:

      #undef  MODULE
      +#define MODULE WEMOS
      +#define USE_UFILESYS
      +#define GUI_TRASH_FILE
      +
      Also recommended, if you use lots of vars and increased buffer sizes to free up the image from unused drivers. You should get some inspiration from the tasmota-lite image definition in tasmota_configurations.h.

      Tip

      You can dump to your PC the raw data coming in if you use the module's hardware serial ports (1 and 3) as GPIOs of the script, using Serial to TCP Bridge. Compile your firmware with USE_TCP_BRIDGE, disable the script and configure in module parameters TCP Tx and TCP Rx. After module reboot, start the server with command TCPStart 8888. Connect to this port from your PC to see or dump the data, in Linux it's as easy as cat < /dev/tcp/IP.OF.YOUR.TASMOTA/8888 > rawdump.txt. To revert to SML you need to set back both GPIO ports to None, enable the script and restart.

      Commands~

      • sensor53 r = resets the sml driver, must be applied in script >B section
      • sensor53 cx num = sets counter x (1 or 2) to number (persistent change)
      • sensor53 dm = sets dump mode for meter m (1...N), must be set to 0 for normal operation
      • sensor53 l x = set an optional LED GPIO pin to indicate serial activity of a meter, set to 255 for disable
      • sensor53 m x = sets the meter from which to show activity via the l cmd

      Smart Meter Descriptors~

      ABB B23 (MODBus)~

      Meter is basically the same as Janitza B23 with the same interface. It's just missing whole section of registers which are used in Janitza B23 example.

      Beware that A and B MODBus connectors are switched!

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,m,0,9600,ABB,1,10,01035B00,01035B02,01035B04,01035B14,01035B16,01035B18,01035B1A,r010350080004,r010350000004,r010350040004
      +1,010304UUuuUUuu@i0:10,Voltage L1-N,V,Voltage_L1,1
      +1,010304UUuuUUuu@i1:10,Voltage L2-N,V,Voltage_L2,1
      +1,010304UUuuUUuu@i2:10,Voltage L3-N,V,Voltage_L3,1
      +1,010304SSssSSss@i3:100,Active power Total,W,Active_power_Total,2
      +1,010304SSssSSss@i4:100,Active power L1-N,W,Active_power_L1,2
      +1,010304SSssSSss@i5:100,Active power L2-N,W,Active_power_L2,2
      +1,010304SSssSSss@i6:100,Active power L3-N,W,Active_power_L3,2
      +1,010308xxxxxxxxSSssSSss@i7:100,Real energy,kWh,Real_energy,2
      +1,010308xxxxxxxxUUuuUUuu@i8:100,Real energy consumed,kWh,Real_energy_consumed,2
      +1,010308xxxxxxxxUUuuUUuu@i9:100,Real energy delivered,kWh,Real_energy_delivered,2
      +#
      +

      ABB B-Series (like B21, B23) (M-Bus over Infrared port)~

      Using the IR Port on left side of the device. The IR interface does NOT support MODBUS, only M-Bus (Meter Bus) and EQ-Bus protocol. Configure the IR output at the device menu. This example is using 9600 baud and address 10h (16 decimal) for the meter.
      The meter is using equal parity 1 stop bit 9600E1 The upper diode is TX the lower RX. My device is sending always 2 telegrams. I tried to add additional values by sending SND_UD telegram. I only receive the e5 response showing that the request was accepted. No change in response. The last telegram will end on a "0F xx 16" instead of a "1F xx 16", which will show that additional telegrams are available. If you can receive more telegrams, add alternating 107b108b16 - 105b106b16. One for each telegram. This example will only work with address 10! the second last byte is a check sum. For this REQ_UD2 it is the sum of the 2 bytes before (Address and VIF).

      View script
      >M 1
      ++1,3,rE1,0,9600,ABB,1,10,1040105016,107b108b16,105b106b16[,107b108b16[,105b106b16]] 
      +1,081072bcd8@1,Meter ID,,ID,0 ; meter ID (BCD-8)
      +1,0E8400bcd8@100,E Imp total,kWh,Imp,2 ; Total imported energy 0.01 kWh
      +1,04A900ssSSssSSs@100,P total,W,P_tot,2 ; Total Power 0.01 W
      +1,04A9FF8100ssSSssSSs@100,P L1,W,P_L1,2 ; L1 Power 0.01 W
      +1,04A9FF8200ssSSssSSs@100,P L2,W,P_L2,2 ; L2 Power 0.01 W
      +1,04A9FF8300ssSSssSSs@100,P L3,W,P_L3,2 ; L3 Power 0.01 W
      +1,04FDC8FF8100uuUUuuUUs@10,U L1,V,U_L1,1 ; Voltage L1 0.1 V
      +1,04FDC8FF8200uuUUuuUUs@10,U L2,V,U_L2,1 ; Voltage L2 0.1 V
      +1,04FDC8FF8300uuUUuuUUs@10,U L3,V,U_L3,1 ; Voltage L3 0.1 V
      +1,0AFFD900bcd4@100,*,Hz,F,2 ; Frequency
      +1,0E84FF8100bcd8@100,E Imp L1,kWh,Imp-L1,2 ; L1 imported energy 0.01 kWh
      +1,0E84FF8200bcd8@100,E Imp L2,kWh,Imp-L2,2 ; L2 imported energy 0.01 kWh 
      +1,0E84FF8300bcd8@100,E Imp L3,kWh,Imp-L3,2 ; L3 imported energy 0.01 kWh
      +#
      +

      Apator APOX+ (SML)~

      Energy provider supplied a PIN code to enable output of additional data.

      View script
      >D  
      +>B  
      +->sensor53 r
      +>M 1  
      ++1,3,s,0,9600,SML
      +1,77070100010801ff@1000,Verbrauch_Tarif_1,kWh,Total_Tarif1,3
      +1,77070100010802ff@1000,Verbrauch_Tarif_2,kWh,Total_Tarif2,3
      +1,77070100010800ff@1000,Verbrauch_Summe,kWh,Total_Summe,3
      +1,77070100100700ff@1,Current consumption,W,Power_curr,3
      +1,=h   ----  
      +1,770701001f0700ff@1,Current L1,A,Curr_p1,3  
      +1,77070100330700ff@1,Current L2,A,Curr_p2,3  
      +1,77070100470700ff@1,Current L3,A,Curr_p3,3  
      +1,=h   ----  
      +1,77070100200700ff@1,Voltage L1,V,Volt_p1,3 
      +1,77070100340700ff@1,Voltage L2,V,Volt_p2,3  
      +1,77070100480700ff@1,Voltage L3,V,Volt_p3,3
      +#
      +

      Apator 12EC3~

      Energy provider supplied a PIN code to enable output of additional data.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,o,0,300,Strom,1,30,2F3F210D0A,063030300D0A
      +1,1.8.0*00(@1,Gesamtverbrauch,KWh,Pges,2
      +1,1.8.1*00(@1,Tagesverbrauch,KWh,Total_day,2
      +1,1.8.2*00(@1,Nachtverbrauch,KWh,Total_night,2
      +1,2.8.0*00(@1,Einspeisung,KWh,Total_out,2
      +#
      +

      Apator 12EC3G~

      No PIN code needed for output data. It can only display total consumption.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,o,0,300,Strom,1,30,2F3F210D0A,063030300D0A
      +1,1.8.0*00(@1,Gesamtverbrauch,KWh,Pges,2
      +#
      +

      Carlo Gavazzi EM340 (MODBUS RTU)~

      View script
      >D
      +>B
      +->sensor53 r
      +;->sensor53 d1
      +>M 1
      ++1,13,m,0,115200,MODBUS,12,2,01030000,01030002,01030004,0103000C,0103000E,01030010,01030012,01030014,01030016,01030018,0103001A,0103001C,0103001E,01030020,01030022,01030034,01030038,0103002e,0103002f,01030030,0103004e
      +1,010304SSssSSsss@i0:10,Voltage L1,V,Voltage_L1,1
      +1,010304SSssSSsss@i1:10,Voltage L2,V,Voltage_L2,1
      +1,010304SSssSSsss@i2:10,Voltage L3,V,Voltage_L3,1
      +1,010304SSssSSsss@i3:1000,Current L1,A,Current_L1,3
      +1,010304SSssSSsss@i4:1000,Current L2,A,Current_L2,3
      +1,010304SSssSSsss@i5:1000,Current L3,A,Current_L3,3
      +1,010304SSssSSsss@i6:10,Power L1,W,Power_L1,1
      +1,010304SSssSSsss@i7:10,Power L2,W,Power_L2,1
      +1,010304SSssSSsss@i8:10,Power L3,W,Power_L3,1
      +1,010304SSssSSsss@i9:10,Power VA L1,VA,Power_va_L1,1
      +1,010304SSssSSsss@i10:10,Power VA L2,VA,Power_va_L2,1
      +1,010304SSssSSsss@i11:10,Power VA L3,VA,Power_va_L3,1
      +1,010304SSssSSsss@i12:10,Power var L1,var,Power_var_L1,1
      +1,010304SSssSSsss@i13:10,Power var L2,var,Power_var_L2,1
      +1,010304SSssSSsss@i14:10,Power var L3,var,Power_var_L3,1
      +1,010304SSssSSsss@i15:10,Energy Tot,kWh,Energy_Tot,1
      +1,010304SSssSSsss@i16:10,Energy Demand,W,Energy_Demand,1
      +1,010304SSss@i17:1000,PF L1,PF,PF_L1,1
      +1,010304SSss@i18:1000,PF L2,PF,PF_L2,1
      +1,010304SSss@i19:1000,PF L3,PF,PF_L3,1
      +1,010304SSssSSsss@i20:10,Energy Tot Export,kWh,Energy_Tot_Export,1
      +#
      +

      COMBO Meter (Water, Gas, SML)~

      View script
      >D  
      +>B  
      +->sensor53 r
      +>M 3  
      ++1,1,c,0,10,H20  
      ++2,4,c,0,50,GAS  
      ++3,3,s,0,9600,SML  
      +1,1-0:1.8.0*255(@10000,Water reading,cbm,Count,4  
      +2,=h==================  
      +2,1-0:1.8.0*255(@100,Gas reading,cbm,Count,3  
      +3,77070100010800ff@1000,Total consumption,KWh,Total_in,3  
      +3,=h==================  
      +3,77070100100700ff@1,Current consumption,W,Power_curr,2  
      +3,=h   ----  
      +3,=m 10+11+12 @100,Currents L1+L2+L3,A,Curr_summ,2  
      +3,=m 13+14+15/#3 @100,Voltage L1+L2+L3/3,V,Volt_avg,2  
      +3,=h==================  
      +3,77070100240700ff@1,Consumption P1,W,Power_p1,2  
      +3,77070100380700ff@1,Consumption P2,W,Power_p2,2  
      +3,770701004c0700ff@1,Consumption P3,W,Power_p3,2  
      +3,=h   ----  
      +3,770701001f0700ff@100,Current L1,A,Curr_p1,2  
      +3,77070100330700ff@100,Current L2,A,Curr_p2,2  
      +3,77070100470700ff@100,Current L3,A,Curr_p3,2  
      +3,=h   ----  
      +3,77070100200700ff@100,Voltage L1,V,Volt_p1,2  
      +3,77070100340700ff@100,Voltage L2,V,Volt_p2,2  
      +3,77070100480700ff@100,Voltage L3,V,Volt_p3,2  
      +3,=h==================  
      +3,77070100000009ff@#,Service ID,,Meter_id,0  
      +3,=h     
      +#  
      +

      DDM18SD (MODBus)~

      This script, based on tasmota scripting language, is to read data on a unsupported DDM18SD power meter. It has 8E1 parity and the slave ID address is n 5. On a Nodemcu or Wemos D1 board, put wires between GPIO3 and GPIO1 to the RX and TX pinout of a RS485 to TTL board, but leave empty (none) the GPIO Tasmota software settings. A & B connected to the meter pinout.

      View script
      >B  
      +->sensor53 r
      +>M 1  
      ++1,3,M,0,9600,DDM,1,2,05040000,05040008,05040012,0504001A,05040036,0504002A,05040100,05040400
      +1,050404ffffffff@i0:1,Tensione,V,DDM_Voltage,2  
      +1,050404ffffffff@i1:1,Corrente,A,DDM_Current,2  
      +1,050404ffffffff@i2:1,Consumo Ist.,W,DDM_Power,2
      +1,050404ffffffff@i3:1,Reactive power,Var,DDM_React_Power,2
      +1,050404ffffffff@i4:1,Frequenza,Hz,DDM_Frequency,2 
      +1,050404ffffffff@i5:1,Power factor,,DDM_PF,2
      +1,050404ffffffff@i6:1,Consumi tot.,Kwh,DDM_Tot_Power,2
      +1,050404ffffffff@i7:1,Tot. react. power,Kvarh,DDM_Reac_Power,2
      +#
      +

      Digimeto GS303 (SML)~

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,0,9600,GS303
      +1,77070100010800ff@1000,Total Consumed,KWh,Total_in,3
      +1,77070100100700ff@1,Current Consumption,W,Power_cur,0
      +1,77070100020800ff@1000,Total Delivered,KWh,Total_out,3
      +1,7707010060320101@#,Service ID,,Meter_id,0
      +#    
      +

      DZG DWS7410.2V.G2 (SML) and DZG DVS7420.2 (SML)~

      A bidirectional metering device from DZG Metering GmbH.

      Once unlocked with a PIN and set to Inf on, the meter returns not only an integer of the total consumption, but an extended dataset which also includes decimals as well as the current power.

      View script for the extended dataset
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,16,9600,DWS7410
      +1,77070100010800ff@1000,Energie,kWh,energy,4
      +1,77070100020800ff@1000,Lieferung,kWh,en_out,4
      +1,77070100100700ff@1,Leistung,W,power,2
      +1,7707010060320101@#,SID,,meter_id,0
      +#
      +

      The script was derived from the DZG DWS76 (SML) device below and extended by the delivered energy. The lines for meter_id, unknown and meter_number were reduced to one line for meter_id because all values were identical.

      For Inf off, a simplified dataset is returned only.

      Alternative script for the simplified dataset
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,16,9600,DWS7410
      +1,77070100010800ff@1000,Energie,kWh,energy,0
      +1,7707010060320101@#,Service ID,,meter_id,0
      +1,77010b0a01445a47@#,Unbekannt,,unknown,0
      +1,77070100600100ff@#,Zählernummer,,meter_number,0
      +#
      +

      DZG DWS7412.1.G2 (SML)~

      A uni-directional metering device from DZG Metering GmbH.

      Once unlocked with a PIN and set to Inf on, the meter returns not only an integer of the total consumption, but an extended dataset which also includes decimals as well as the current power.

      View script for the extended dataset
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,16,9600,DWS7412
      +1,77070100010800ff@1000,Energy,kWh,energy,4
      +1,77070100240700ff@1,Power,W,power,2
      +1,7707010060320101@#,Service ID,,meter_id,0
      +#
      +

      For Inf off, a simplified dataset is returned only.

      Alternative script for the simplified dataset
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,16,9600,DWS7412
      +1,77070100010800ff@1000,Energy,kWh,energy,4
      +1,7707010060320101@#,Service ID,,meter_id,0
      +#
      +

      DZG DWS76 (SML)~

      Should also be applicable to many other DZG Metering GmbH meters, like DVS76, DVS74, DWS74 or in general DxS7x - not tested though.

      Once unlocked with a PIN and set to Inf on, the meter returns not only an integer of the total consumption, but an extended dataset which also includes decimals as well as the current power. Further values unknown yet.

      View script for the extended dataset
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,16,9600,DWS7612
      +1,77070100010800ff@1000,Energie,kWh,energy,4
      +1,77070100100700ff@1,Leistung,W,power,2
      +1,7707010060320101@#,Service ID,,meter_id,0
      +1,77010b0a01445a47@#,Unbekannt,,unknown,0
      +1,77070100600100ff@#,Zählernummer,,meter_number,0
      +#  
      +

      For Inf off, a simplified dataset is returned only.

      Alternative script for the simplified dataset
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,16,9600,DWS7612
      +1,77070100010800ff@1000,Energie,kWh,energy,0
      +1,7707010060320101@#,Service ID,,meter_id,0
      +1,77010b0a01445a47@#,Unbekannt,,unknown,0
      +1,77070100600100ff@#,Zählernummer,,meter_number,0
      +#
      +

      DZG DWSB12.2 (SML)~

      For Inf off, a simplified dataset is returned. The energy meter does have 2 meters, one for incoming and outgoing power. If you have a solar panel installed and you are delivering power to the network the second value will increase appropriately.

      Alternative script for the simplified dataset
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,16,9600,DWSB122
      +1,77070100010800ff@1000,Energie bezogen,kWh,energy,0
      +1,77070100020800ff@1000,Energie geliefert,kWh,energy,0
      +1,7707010060320101@#,Service ID,,meter_id,0
      +1,77010b0a01445a47@#,Unbekannt,,unknown,0
      +1,77070100600100ff@#,Zählernummer,,meter_number,0
      +#
      +

      EasyMeter Q3A / Apator APOX+ (SML)~

      A 2-Tariff Meter which for Example SWM (Stadtwerke München) oder DGY (Discovergy) uses. Unfortunately this Version sends only whole kWh (precision 0) without PIN. With PIN behaviour changes and high resolution is available as seen below (e.g. precision 7 for consumption/kWh, precision 2 for power/W, precision 1 for voltage/V).

      Apator APOX+ behaves same as the EasyMeter while pin locked, just precision 0 without additional data. After calling the energy provider they send a letter with the unlock pin.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,0,9600,SML
      +1,77070100010801ff@1000,Verbrauch_Tarif_1,kWh,Verbrauch_T1,7
      +1,77070100010802ff@1000,Verbrauch_Tarif_2,kWh,Verbrauch_T2,7
      +1,77070100010800ff@1000,Verbrauch_Summe,kWh,Verbrauch_Summe,7
      +1,77070100020800ff@1000,Einspeisung_Summe,kWh,Einspeisung_Summe,7
      +1,=h-- 
      +1,77070100240700ff@1,Leistung_L1,W,Watt_L1,2
      +1,77070100380700ff@1,Leistung_L2,W,Watt_L2,2
      +1,770701004c0700ff@1,Leistung_L3,W,Watt_L3,2
      +1,77070100100700ff@1,Leistung_Summe,W,Watt_Summe,2
      +1,=h-- 
      +1,77070100200700ff@1,Spannung_L1,V,Volt_L1,1
      +1,77070100340700ff@1,Spannung_L2,V,Volt_L2,1
      +1,77070100480700ff@1,Spannung_L3,V,Volt_L3,1
      +#
      +

      EasyMeter Q3B (SML)~

      Two separate 2-Tariff meters (e.g. from Fairenergie Reutlingen) are readout by the same Tasmota device. The first one is for general purpose and is connected to GPIO14. The JSON prefix is set to Power. The second one is for the heat pump and connected to GPIO13. The JSON prefix is set to Pump. For both meters, tariff 1 & 2 are rounded kWh (precision 0), actual consumption in W has a higher precision (1).

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 2
      ++1,14,s,0,9600,Power
      +1,77070100010801ff@1000,Tarif 1,kWh,Power_T1,0
      +1,77070100010802ff@1000,Tarif 2,kWh,Power_T2,0
      +1,77070100010800ff@1000,Summe,kWh,Power_Sum,0
      +1,77070100010700ff@1000,Verbrauch,W,Power_Use_Sum,1
      ++2,13,s,0,9600,Pump
      +2,77070100010801ff@1000,Tarif 1,kWh,HP_T1,0
      +2,77070100010802ff@1000,Tarif 2,kWh,HP_T2,0
      +2,77070100010800ff@1000,Summe,kWh,HP_Sum,0
      +2,77070100010700ff@1000,Verbrauch,W,HP_Use_Sum,1 
      +#
      +

      EasyMeter Q3D, Q3DA1024 (OBIS)~

      The Q3D is a three-phase model energy meter, which was sold in a number of different configurations. This is a legacy device, however still available new in some shops. The most popular model seems to be the two-direction model for solar power metering. The D0 port is read-only with a fixed time interval of two seconds. The communication settings are unusual: 7 data bits, even parity, one stop bit, 9600 baud (9600 7E1).

      Because the 7E1 serial mode is not supported by Tasmota software serial, the hardware serial port must be used, i.e. GPIO 3. This will /not/ work using GPIO 0 or 2.

      Also, the source code has to be patched from 8N1 to 7E1 mode for the hardware serial in file src/TasmotaSerial.cpp, please see the patch further down below. Since Tasmota 9.5.0 the serial mode can be changed in the console by typing SerialConfig 7E1 without having to patch TasmotaSerial.

      Example reading of the two-direction model using GPIO 3 - P_in power reading will be negative in case of inverse power flow:
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,12,o,0,9600,SML,1
      +1,1-0:1.7.255*255(@1,P_in,W,P_in,18
      +1,1-0:21.7.255*255(@1,L1,W,L1,18
      +1,1-0:41.7.255*255(@1,L2,W,L2,18
      +1,1-0:61.7.255*255(@1,L3,W,L3,18
      +1,1-0:1.8.0*255(@1,E_in,kWh,E_in,19
      +1,1-0:2.8.0*255(@1,E_out,kWh,E_out,19
      +1,1-0:0.0.0*255(@1,Netzbetreiber-ID,,NetID,0
      +1,0-0:96.1.255*255(@#),Seriennummer,,serial,0
      +#
      +
      Alternative script running on a Wemos D1 mini on hardware serial pin 3 for the Q3DB1024 two direction
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,o,0,9600,Haupt,1
      +1,1-0:1.7.0*255(@1,P_in,W,P_in,18
      +1,1-0:1.8.0*255(@1,E_in,kWh,E_in,19
      +1,1-0:2.8.0*255(@1,E_out,kWh,E_out,19
      +1,1-0:21.7.0*255(@1,L1,W,L1,18
      +1,1-0:41.7.0*255(@1,L2,W,L2,18
      +1,1-0:61.7.0*255(@1,L3,W,L3,18
      +1,1-0:0.0.0*255(@1,Netzbetreiber-ID,,NetID,0
      +1,0-0:96.1.255*255(@#),Seriennummer,,serial,0
      +#    
      +
      Apply following patch to src/TasmotaSerial.cpp
      --- a/lib/default/TasmotaSerial-3.2.0/src/TasmotaSerial.cpp
      ++++ b/lib/default/TasmotaSerial-3.2.0/src/TasmotaSerial.cpp
      +@@ -117,7 +117,7 @@ bool TasmotaSerial::begin(long speed, int stop_bits) {
      +    if (2 == m_stop_bits) {
      +    Serial.begin(speed, SERIAL_8N2);
      +    } else {
      +-      Serial.begin(speed, SERIAL_8N1);
      ++      Serial.begin(speed, SERIAL_7E1);
      +    }
      +    if (m_hardswap) {
      +    Serial.swap();
      +

      eBZ DD3 (OBIS)~

      The eBZ DD3 by eBZ GmbH is a three-phase model energy meter, which is sold in a number of different configurations. The D0 port is read-only with a fixed time interval of one second.

      There are two communication interfaces: * The INFO interface on the front, with a metal backplate. Pushes a reduces OBIS ASCI datagram every second. * The MSB interface on the top, no metal backplate. Pushes a full OBIS ASCI datagram every second.

      There are two types available using different communication settings: * OD-type: 7 data bits, even parity, one stop bit, 9600 baud (9600 7E1) * SM-type: 8 data bits, no parity, one stop bit, 9600 baud (9600 8N1)

      Tested with an eBZ DD3 2R06 ODZ1 (two-direction model for e. g. solar power metering).

      Because the 7E1 serial mode is not supported by Tasmota software serial, the hardware serial port must be used, i.e. GPIO 3. This will /not/ work using GPIO 0 or 2.

      Change the serial mode in the console by typing SerialConfig 7E1.

      Example reading of the two-direction model using GPIO 3:

      • "TelePeriod 30" sets telemetry period to 30 seconds (remove if not needed/wanted)
      • Values for ?6.7.0 (power) are transmit immediately (precision + 16)
      • power readings will be negative in case of inverse power flow
      View script
      >D
      +>B
      +TelePeriod 30
      +=>sensor53 r
      +>M 1
      +; Device: eBZ DD3 2R06 ODZ1
      +; protocol is D0 OBIS ASCII
      +; 9600@7E1 for OP-type devices, 9600@8N1 for SM-type devices
      ++1,3,o,0,9600,SM,1
      +; Zählerstand zu +A, tariflos, 
      +; Zählerstände Auflösung 10 µW*h (6 Vorkomma- und 8 Nachkommastellen)
      +1,1-0:1.8.0*255(@0.001,Energie Bezung,Wh,1_8_0,8
      +; Zählerstand zu +A, Tarif 1
      +1,1-0:1.8.1*255(@0.001,Energie Bezung T1,Wh,1_8_1,8
      +; Zählerstand zu +A, Tarif 2
      +1,1-0:1.8.2*255(@0.001,Energie Bezung T2,Wh,1_8_2,8
      +; Zählerstand zu -A, tariflos
      +1,1-0:2.8.0*255(@0.001,Energie Export,Wh,2_8_0,8
      +; Summe der Momentan-Leistungen in allen Phasen, Auflösung 0,01W (5 Vorkomma- und 2 Nachkommastellen)
      +1,1-0:16.7.0*255(@1,Leistung,W,16_7_0,18
      +; Momentane Leistung in Phase Lx, Auflösung 0,01W (5 Vorkomma- und 2 Nachkommastellen)
      +1,1-0:36.7.0*255(@1,Leistung L1,W,36_7_0,18
      +1,1-0:56.7.0*255(@1,Leistung L2,W,56_7_0,18
      +1,1-0:76.7.0*255(@1,Leistung L3,W,76_7_0,18
      +; Spannung in Phase Lx, Auflösung 0,1V (nur über MSB)
      +1,1-0:32.7.0*255(@1,Spannung L1,V,32_7_0,1
      +1,1-0:52.7.0*255(@1,Spannung L2,V,52_7_0,1
      +1,1-0:72.7.0*255(@1,Spannung L3,V,72_7_0,1
      +; Statuswort, 4 Byte Information über den Betriebszustand, HEX string
      +; tasmota can decode one string per device only!
      +;1,1-0:96.5.0*255(@#),Status1,,96_5_0,0
      +;1,1-0:96.8.0*255(@#),Status2,,96_8_0,0
      +; Geräte-Identifikation, Nach DIN 43863-5 
      +1,1-0:96.1.0*255(@#),Identifikation,,96_1_0,0
      +;1,1-0:0.0.0*255(@#),Identifikation,,0_0_0,0
      +#
      +
      For the SM-type meter DD3 2R06 DTA SMZ1 the following script worked without having to apply the above patch, because it uses 8N1 for communication
      >D
      +>B
      +;TelePeriod 30
      +=>sensor53 r
      +>M 1
      +; Device: eBZ DD3 2R06 DTA SMZ1
      +; protocol is D0 SML HEX
      +; 9600@7E1 for OP-type devices, 9600@8N1 for SM-type devices
      ++1,3,s,0,9600,SML,1
      +; Zählerstand zu +A, tariflos, 
      +; Zählerstände Auflösung 10 µW*h (6 Vorkomma- und 8 Nachkommastellen)
      +1,77070100010800FF@100000000,Energie Bezug,kWh,1_8_0,8
      +; Zählerstand zu +A, Tarif 1
      +1,77070100010801FF@100000000,Energie Bezug T1,kWh,1_8_1,8
      +; Zählerstand zu +A, Tarif 2
      +1,77070100010802FF@100000000,Energie Bezug T2,kWh,1_8_2,8
      +; Zählerstand zu -A, tariflos
      +1,77070100020800FF@100000000,Energie Export,kWh,2_8_0,8
      +; Summe der Momentan-Leistungen in allen Phasen, Auflösung 0,01W (5 Vorkomma- und 2 Nachkommastellen)
      +1,77070100100700FF@1,Leistung,W,16_7_0,16
      +; Momentane Leistung in Phase Lx, Auflösung 0,01W (5 Vorkomma- und 2 Nachkommastellen)
      +1,77070100240700FF@1,Leistung L1,W,36_7_0,16
      +1,77070100380700FF@1,Leistung L2,W,56_7_0,16
      +1,770701004C0700FF@1,Leistung L3,W,76_7_0,16
      +; Spannung in Phase Lx, Auflösung 0,1V (nur über MSB)
      +;1,77070100200700FF@1,Spannung L1,V,32_70,1
      +;1,77070100340700FF@1,Spannung L2,V,52_7_0,1
      +;1,77070100480700FF@1,Spannung L3,V,72_7_0,1
      +; Statuswort, 4 Byte Information über den Betriebszustand, HEX string
      +; tasmota can decode one string per device only!
      +;1,1-0:96.5.0*255@#),Status1,,96_5_0,0
      +;1,1-0:96.8.0*255@#),Status2,,96_8_0,0
      +; Geräte-Identifikation, Nach DIN 43863-5 
      +1,77070100000009FF@#),Identifikation,,96_1_0,0
      +;1,77070100000000FF@#),Identifikation,,0_0_0,0
      +#
      +

      EasyMeter Q1A (SML)~

      The Q1A series of EasyMeter is available as one- or two-way meter, with and without backstop respectively. It is also available as single or dual tariff meter. The script below works for the Q1Ax1054 variant. This variant is a single-tariff one-way meter with a backstop mechanism. The script only reads two values: the energy counter value and the current power value.

      The meter has no bidirectional IR-communication port, only an "INFO-DSS" send-only IR-LED. It also has no metal plate to attach a magnet so the IR reader has to be attached in another way.

      The current power and counter in high resolution are available after PIN entry with a flashlight, see manual.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,16,9600,SML
      +1,77070100010800ff@1000,Zaehlerstand,kWh,Counter,6
      +1,77070100100700ff@1,Verbrauch,W,Power,1
      +#
      +

      EasyMeter Q1D (ASCII OBIS)~

      This script is for the EasyMeter Q1DB1004 variant of the Q1D series. This variant is a one-phase one-way electricity counter with a backstop mechanism.

      Therefore the script reads only two values: the energy counter value and the power value. The power value is positive when you are drawing power from the public grid, and negative when you are feeding power to the public grid. Due to the backstop mechanism, the energy counter value will not decrease when you feed power to the public grid.

      The meter's manufacturer's datasheet neatly explains the serial message format used, so you can easily adapt the code below to your EasyMeter Q1D, e.g. if you have a two-way counter variant like the EasyMeter Q1DA1026.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,o,0,9600,SML
      +1,1-0:1.8.0*255(@1,EC_CounterVal,kWh,EC_CounterVal,4
      +1,1-0:61.7.255*255(@1,EC_PowerVal,W,EC_PowerVal,0
      +#
      +

      According to the manufacturer's datasheet, the serial parameters are 9600 baud and 7E1.

      For Tasmota versions that are built with a TasmotaSerial.cpp of version 3.5.0 (and probably all higher versions, too), no modification of the TasmotaSerial.cpp source code (as suggested in other entries of this documentation) is necessary to set the serial parameters to 7E1: By configuring the meter type as OBIS ("o") in line 5 of the above code, you implicitly tell Tasmota to set the serial parameters to 7E1 (probably the same applies to all other meters in this documentation where a modification of TasmotaSerial.cpp has previously been recommended).

      EFR SGM-C2/C4 (SML)~

      By default, the energy meter only sends the total energy values. To be able to read all the other values, you need to enter the PIN and set InF on. The PIN must be requested from the metering provider, which is usually your local grid provider and not your energy provider (but they know whom to ask). After entering the PIN and setting InF on, all the values will be available. The jsonPrefix ENERGY and variable names (between second to last and last ,) Total, Power, Voltage and Current are chosen to match the regular MQTT message format, used by tasmota powerplugs. For SGM-C4, double-tariff variants or meters measuring supply remove the appropriate leading ; to uncomment and enable the values you are interested in.

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,16,9600,ENERGY
      +1,77070100010800ff@1000,Comsumption,kWh,Total,4
      +;for meters measuring feed-in SGM-Cx-xxx2xx
      +;1,77070100020800ff@1000,Supply,kWh,Supply,4
      +;next 4 lines for double-tariff meters SGM-Cx-xxxxTx
      +;1,77070100010801ff@1000,Comsumption_t1,kWh,Total_t1,4
      +;1,77070100010802ff@1000,Comsumption_t2,kWh,Total_t2,4
      +;1,77070100020801ff@1000,Supply_t1,kWh,Supply_t1,4
      +;1,77070100020802ff@1000,Supply_t2,kWh,Supply_t2,4
      +;all commented lines from here on for 3-phase meter SGM-C4
      +1,77070100100700ff@1,Actual Power,W,Power,0
      +1,77070100200700ff@1,Voltage L1,V,Voltage,1
      +;1,77070100340700ff@1,Voltage L2,V,Voltage_L2,1
      +;1,77070100480700ff@1,Voltage L3,V,Voltage_L3,1
      +1,770701001f0700ff@1,Current L1,A,Current,2
      +;1,77070100330700ff@1,Current L2,A,Current_L2,2
      +;1,77070100470700ff@1,Current L3,A,Current_L3,2
      +;1,77070100510701ff@1,Phaseangle L2-L1,deg,phase_angle_L2_L1,0
      +;1,77070100510702ff@1,Phaseangle L3-L1,deg,phase_angle_L3_L1,0
      +1,77070100510704ff@1,Phaseangle I/U L1,deg,phase_angle_L1,1 
      +;1,7707010051070fff@1,Phaseangle I/U L2,deg,phase_angle_L2,1  
      +;1,7707010051071aff@1,Phaseangle I/U L3,deg,phase_angle_L3,1 
      +1,770701000e0700ff@1,Frequency,Hz,Freq,1
      +;all commented lines from here on just for completeness
      +;1,7707010000020000@1,Firmware Version,,FwVer,0
      +;1,77070100605a0201@1,Firmware Checksum,,FwCheck,0
      +;1,7707010061610000@1,Error Register,,ErrorReg,0
      +;1,7707010060320101@1,Hardware Version,,HwVer,0
      +;1,7707010060320104@1,Parameter Version,,ParamVer,0
      +1,77070100600100ff@#,Server-ID,,ID,0
      +;You can find your server-id printed on your meter. If you want you can also convert it to your Identifikationsnummer with some ASCII, DEC and HEX encoding. 0A-01-45-46-52-ST-UV-WX-YZ = 1EFR + string(S) + string(T) + hexToDec(UVWXYZ)
      +#
      +

      Overview of the codes image

      Elster / Honeywell AS1440 (OBIS)~

      Based on Landis script with changed timings in the >F section, as AS1440 seems to be slower in responding.

      This defines metrics for totals and current power usage for both incoming and outgoing power. Just delete the lines you don't need from the last sections.
      Current power values get published to mqtt immediately when received from the meter.

      View script
      >D
      +scnt=0
      +res=0
      +
      +>B
      +=>sensor53 r
      +
      +>F
      +; count 100ms
      +scnt+=1
      +switch scnt
      +
      +;300ms after start: set sml driver to 300 baud and send /?! as HEX to trigger the Meter
      +case 3
      +res=sml(1 0 300)
      +res=sml(1 1 "2F3F210D0A")
      +
      +;1700ms later: Ack and ask for switching to 9600 baud
      +case 20
      +res=sml(1 1 "063035300D0A")
      +
      +;300ms later: switch sml driver to 9600 baud
      +case 23
      +res=sml(1 0 9600)
      +
      +;6000ms after start: Restart sequence
      +case 60
      +scnt=0
      +
      +ends
      +
      +>M 1
      ++1,3,o,0,9600,AS1440,1
      +1,1.7.0(@0.001,Power In,W,power_in,16
      +1,1.8.1(@1,Total In,KWh,Total_in,1
      +1,2.7.0(@0.001,Power Out,W,power_out,16
      +1,2.8.1(@1,Total Out,KWh,Total_out,1
      +#
      +

      Elster F96 Plus (Sharky 775) (Ditech Integral-V UltraLite PRO) M-Bus~

      This heat meter needs a wakeup sequence with 2400 Baud 8N1, wheras communication is done by 2400 Baud 8E1. The script will therefore only rund starting with Tasmota 12.2 where switching parity is implemented. For compiling, add the following to your user_config_override.h to increase serial communication buffer size and enable MQTT and Web publishing:

      View user_config_override.h
      #ifndef USE_SCRIPT
      +#define USE_SCRIPT
      +#endif
      +#ifndef USE_SML_M
      +#define USE_SML_M
      +#endif
      +#ifdef USE_RULES
      +#undef USE_RULES
      +#endif
      +#ifndef SML_BSIZ
      +#define SML_BSIZ 200
      +#endif
      +#ifndef USE_SML_SCRIPT_CMD
      +#define USE_SML_SCRIPT_CMD
      +#endif
      +#ifndef USE_SCRIPT_JSON_EXPORT
      +#define USE_SCRIPT_JSON_EXPORT
      +#endif
      +#ifndef USE_SCRIPT_WEB_DISPLAY
      +#define USE_SCRIPT_WEB_DISPLAY
      +#endif
      +

      Delta calculation for previous day is included as the meter shall not be read often when operated with a battery.

      View script
      >D
      +;start, define variables
      +cnt=1
      +timer=1
      +w_new=0
      +w_delta=0
      +p:w_last=0
      +
      +>B
      +;setup sensor
      +->sensor53 r
      +
      +>T
      +w_new=WAERME#w_total
      +
      +>S
      +timer=int(time)
      +if chg[timer]>0 
      +then
      +switch timer
      +case 0
      +print It is midnight
      +print wakeup start
      +sml(-1 1 "2400:8N1")
      +for cnt 1 72 1
      +sml(1 1 "55555555555555555555")
      +next
      +print wakeup end
      +print wait for the meter
      +delay(350)
      +sml(-1 1 "2400:8E1")
      +print request data
      +sml(1 1 "105BFE5916")
      +case 1
      +print It is a minute after midnight
      +print calculating daily value
      +print w_last %0w_last%
      +w_delta=w_new-w_last
      +w_last=w_new
      +svars
      +print w_new %0w_new%
      +print w_delta %0w_delta%
      +ends
      +endif
      +
      +>J  
      +,"w_delta":%w_delta% 
      +
      +>W
      +===============
      +Vortagsverbrauch:    {m} %3w_delta% KWh 
      +
      +>M 1
      ++1,3,rE1,0,2400,WAERME,1
      +1,0C06bcd8@1,Total Energy,kWh,w_total,0
      +1,0C13bcd8@1000,Total volume,m³,v_total,2
      +1,0C2Bbcd8@1,Current power,W,p_act,0
      +1,0B3Bbcd6@1000,Current flow,m³/h,F_akt,3
      +1,0A5Abcd4@10,Flow temp,°C,t_flow,1
      +1,0A5Ebcd4@10,Return temp,°C,t_return,1
      +1,0A62bcd4@10,Temp diff,°C,t_diff,2
      +#
      +

      Elster / Honeywell AS2020 (SML)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,0,9600,,1
      +1,77070100600100ff@#,Server-ID,,Wert,0
      +1,77070100010800ff@1000,Total Consumed,kWh,total_consumed_kwh,1
      +1,77070100020800ff@1000,Total Delivered,kWh,total_delivered_kwh,1
      +1,77070100100700ff@0.1,Current Consumption,W,current_consumption,0
      +#
      +

      Elster T510 (OBIS)~

      It seems you can not read the total power like 1,1.7.0(@0.001,Leistung,W,Power_curr,0, as the value just alternates between the values of the three phases. So in this script the three phases get added and published as Power_total.

      View script
      >D
      +;Var for baudrate changing
      +res=0
      +;Var for counter see >F=ms  
      +scnt=0  
      +
      +>B
      +->sensor53 r
      +;Set teleperiod to 20sec  
      +;tper=10
      +
      +>F
      +; count 100ms   
      +scnt+=1  
      +switch scnt  
      +case 6  
      +;set sml driver to 300 baud and send /?! as HEX to trigger the Meter   
      +res=sml(1 0 300)  
      +res=sml(1 1 "2F3F210D0A")  
      +
      +;1800ms later \> Ack and ask for switching to 9600 baud  
      +case 18  
      +res=sml(1 1 "063035300D0A")  
      +
      +;2000ms later \> Switching sml driver to 9600 baud    
      +case 20  
      +res=sml(1 0 9600)  
      +
      +;Restart sequence after 50x100ms    
      +case 50  
      +; 5000ms later \> restart sequence    
      +scnt=0  
      +ends  
      +
      +>M 1
      +
      ++1,3,o,0,9600,,1
      +1,0.0.0(@1,Zählernummer,,Meter_number,0
      +1,0.9.1(@#),Zeitstempel,Uhr,timestamp,0
      +1,1.8.0(@1,Zählerstand,kWh,Total_in,3
      +1,21.7.0(@0.001,Leistung Phase 1,W,Power_L1,0
      +1,41.7.0(@0.001,Leistung Phase 2,W,Power_L2,0
      +1,61.7.0(@0.001,Leistung Phase 3,W,Power_L3,0
      +1,=m 4+5+6 @1,Leistung,W,Power_total,0
      +1,31.7.0(@1,Strom Phase 1,A,Current_L1,2
      +1,51.7.0(@1,Strom Phase 2,A,Current_L2,2
      +1,71.7.0(@1,Strom Phase 3,A,Current_L3,2
      +#
      +

      EMH eBZD (SML)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,0,9600,Main
      +1,77070100100700ff@1,Power,W,power,0
      +1,77070100010800ff@1000,Total Consumed,kWh,counter_pos,3
      +1,77070100020800ff@1000,Total Feed,kWh,counter_neg,3
      +#
      +

      EMH ED300L (SML)~

      View script
      >D    
      +>B   
      +->sensor53 r  
      +>M 2  
      ++1,13,s,0,9600,Haus  
      ++2,12,s,0,9600,Heizung  
      +1,770701000F0700FF@1,Aktuell,W,Power_curr,0  
      +1,77070100010800FF@1000,Zählerstand Verb.,kWh,Tariflos,2  
      +1,77070100020800FF@1000,Zählerstand Einsp.,kWh,Tariflos,2  
      +2,=h==================  
      +2,770701000F0700FF@1,Aktuell,W,Power_curr,0  
      +2,77070100010800FF@1000,Zählerstand Verb.,kWh,Tariflos,2  
      +2,77070100020800FF@1000,Zählerstand Einsp.,kWh,Tariflos,2  
      +#    
      +

      EMH ED300S (SML)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,0,9600,Main
      +1,77070100100700ff@1,Power,W,power,0
      +1,77070100010800FF@1000,Counter,kWh,counter,3
      +#
      +

      EMH eHZ Generation K (SML)~

      View script
      >D
      +>B
      +
      +=>sensor53 r
      +>M 1
      ++1,3,s,0,9600,
      +1,77070100010800ff@1000,Gesamtverbrauch,KWh,Total_in,2
      +1,77070100020800ff@1000,Gesamteinspeisung,KWh,Total_out,2
      +1,77070100100700ff@1,Verbrauch,W,Power_curr,0
      +#
      +

      EMH metering - eHZM (SML)~

      Website

      Datasheet

      Manual (+OBIS Registers)

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,0,9600,
      +1,77070100600100FF@#,Zaehlernummer,,serialnr,16
      +1,77070100010800FF@1000,Pos Wirkenergie tariflos,kWh,pos_wirk_tariflos,1
      +1,77070100010801FF@1000,Pos Wirkenergie Tarif 1,kWh,pos_wirk_tarif_1,1
      +1,77070100010802FF@1000,Pos Wirkenergie Tarif 2,kWh,pos_wirk_tarif_2,1
      +1,77070100020800FF@1000,Neg Wirkenergie tariflos,kWh,neg_wirk_tariflos,1
      +1,77070100020801FF@1000,Neg Wirkenergie Tarif 1,kWh,neg_wirk_tarif_1,1
      +1,77070100020802FF@1000,Neg Wirkenergie Tarif 2,kWh,neg_wirk_tarif_2,1
      +1,77070100100700FF@1,Momentanwirkleistung,W,momentanwirkleistung,0
      +#
      +

      EMH mMe4.0 (SML)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,0,9600,Main,1,10
      +1,77070100010800FF@1000,Total Consumed,kWh,counter_pos,3
      +1,77070100020800FF@1000,Total Feed,kWh,counter_neg,3
      +1,77070100100700FF@1,Power,W,power,0
      +#
      +

      eZB MD3 (SML)~

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,0,9600,Smartmeter
      +1,77070100010800ff@100000000,Total consumption,KWh,Total_in,3
      +1,77070100020800ff@100000000,Total generation,KWh,Total_out,3
      +1,77070100100700ff@1,Power L1+L2+L3,W,P_L1_L2_L3,18
      +1,77070100240700ff@1,Power L1,W,P_L1,18
      +1,77070100380700ff@1,Power L2,W,P_L2,18
      +1,770701004C0700ff@1,Power L3,W,P_L3,18
      +#
      +

      Fronius Symo 10.0-3-M (MODBUS)~

      Fronius inverter, using Modbus TCP feature

      View script
      >D 48
      +>B
      +=>sensor53 r
      +>M 2
      ++1,[192.168.3.38],m,0,502,mod1,1,10,r01039C870015
      +1,=so3,128
      +1,01032aUUuu@i0:1,AC Current,A,AC_Current,0
      +1,01032ax2UUuu@i0:1,Phase 1 Current,A,Phase_1_Current,0
      +1,01032ax4UUuu@i0:1,Phase 2 Current,A,Phase_2_Current,0
      +1,01032ax6UUuu@i0:1,Phase 3 Current,A,Phase_3_Current,0
      +1,01032ax8SSss@i0:1,Curr Scale Fctr,SF,Curr_SF,0
      +1,01032ax16UUuu@i0:1,Phase 1 Voltage,A,Phase_1_Voltage,0
      +1,01032ax18UUuu@i0:1,Phase 2 Voltage,A,Phase_2_Voltage,0
      +1,01032ax20UUuu@i0:1,Phase 3 Voltage,A,Phase_3_Voltage,0
      +1,01032ax22SSss@i0:1,Vltg Scale Fctr,SF,Vltg_SF,0
      +1,01032ax24UUuu@i0:1,Output Power,W,Output_Power,0
      +1,01032ax26SSss@i0:1,Pwr Scale Fctr,SF,Pwr_SF,0
      +1,01032ax28UUuu@i0:1,Frequency,Hz,Frequency,0
      +1,01032ax30SSss@i0:1,Freq Scale Fctr,SF,Freq_SF,0
      +1,01032ax36UUuu@i0:1,Temperature,C,Temperature,0
      +1,01032ax38SSss@i0:1,Temp Scale Fctr,SF,Temp_SF,0
      ++2,[192.168.3.23],m,0,502,mod2,1,10,r01039C870015
      +2,=so3,128
      +2,01032aUUuu@i0:1,AC Current,A,AC_Current,0
      +2,01032ax2UUuu@i0:1,Phase 1 Current,A,Phase_1_Current,0
      +2,01032ax4UUuu@i0:1,Phase 2 Current,A,Phase_2_Current,0
      +2,01032ax6UUuu@i0:1,Phase 3 Current,A,Phase_3_Current,0
      +2,01032ax8SSss@i0:1,Curr Scale Fctr,SF,Curr_SF,0
      +2,01032ax16UUuu@i0:1,Phase 1 Voltage,A,Phase_1_Voltage,0
      +2,01032ax18UUuu@i0:1,Phase 2 Voltage,A,Phase_2_Voltage,0
      +2,01032ax20UUuu@i0:1,Phase 3 Voltage,A,Phase_3_Voltage,0
      +2,01032ax22SSss@i0:1,Vltg Scale Fctr,SF,Vltg_SF,0
      +2,01032ax24UUuu@i0:1,Output Power,W,Output_Power,0
      +2,01032ax26SSss@i0:1,Pwr Scale Fctr,SF,Pwr_SF,0
      +2,01032ax28UUuu@i0:1,Frequency,Hz,Frequency,0
      +2,01032ax30SSss@i0:1,Freq Scale Fctr,SF,Freq_SF,0
      +2,01032ax36UUuu@i0:1,Temperature,C,Temperature,0
      +2,01032ax38SSss@i0:1,Temp Scale Fctr,SF,Temp_SF,0
      +#
      +

      Growatt MAX4200 (MODBus)~

      Growatt solar inverter. this example also shows how to send cmds to modbus

      View script
      >D 22
      +
      +cstr=""
      +gl=0
      +tmp=0
      +
      +>B
      +=>sensor53 r
      +
      +
      +>S
      +if chg[gl]>0 {
      +    ; change limit
      +    tmp=int(gl/42)
      +    cstr="r0106000300"+hn(tmp)
      +    sml(1 3 cstr)
      +}
      +
      +>M 1
      ++1,18,m,0,9600,GRW,19,5,01040026,01040028,01040005,01040009,01030003
      +1,010404UUuu@i0:10,Netzspannung,V,mainsv,1
      +1,010404xxxxUUuu@i0:10,Einspeisestrom,A,mainsc,1
      +1,010404UUuuUUuu@i1:10,Einspeiseleistung,W,mainsw,1
      +1,010404UUuuUUuu@i2:10,string 1 unten,W,s1w,1
      +1,010404UUuuUUuu@i3:10,string 2 oben,W,s2w,1
      +1,010304UUuu@i4:1,limit,%,limit,0
      +#
      +
      +>W
      +<hr>
      +nm(1000 3600 10 gl "Growatt limit (W) " 80 0) 
      +

      Hager EHZ161 / EHZ361 (OBIS)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,o,0,9600,OBIS
      +1,1-0:0.0.0*255(@#),Meter Number,,meter_number,0
      +1,1-0:1.8.0*255(@1,Power Consumption A+,kWh,zaehler_a+,4
      +1,1-0:2.8.0*255(@1,Power Feed A-,kWh,zaehler_a-,4
      +1,1-0:32.7.0*255(@1,Volt_L1,V,volt_l1,2
      +1,1-0:52.7.0*255(@1,Volt_L2,V,volt_l2,2
      +1,1-0:72.7.0*255(@1,Volt_L3,V,volt_l3,2
      +1,1-0:31.7.0*255(@1,Ampere_L1,A,volt_l1,2
      +1,1-0:51.7.0*255(@1,Ampere_L2,A,volt_l2,2
      +1,1-0:71.7.0*255(@1,Ampere_L3,A,volt_l3,2
      +1,1-0:21.7.0*255(@1,Watt_L1,W,watt_l1,0
      +1,1-0:41.7.0*255(@1,Watt_L2,W,watt_l2,0
      +1,1-0:61.7.0*255(@1,Watt_L3,W,watt_l3,0
      +#
      +

      Hager EHZ363, Apator Norax 3D (SML)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,0,9600,SML
      +1,77070100010800ff@1000,Total consumption,KWh,Total_in,4
      +1,77070100020800ff@1000,Total Feed,KWh,Total_out,4
      +1,77070100100700ff@1,Current consumption,W,Power_curr,0
      +1,77070100200700ff@1,Voltage L1,V,Volt_p1,1
      +1,77070100340700ff@1,Voltage L2,V,Volt_p2,1
      +1,77070100480700ff@1,Voltage L3,V,Volt_p3,1
      +1,770701001f0700ff@1,Amperage L1,A,Amperage_p1,1
      +1,77070100330700ff@1,Amperage L2,A,Amperage_p2,1
      +1,77070100470700ff@1,Amperage L3,A,Amperage_p3,1
      +1,77070100510704ff@1,Phaseangle I-L1/U-L1,deg,phase_angle_p1,1 
      +1,7707010051070fff@1,Phaseangle I-L27I-L2,deg,phase_angle_p2,1  
      +1,7707010051071aff@1,Phaseangle I-L3/I-L3,deg,phase_angle_p3,1 
      +1,770701000e0700ff@1,Frequency,Hz,frequency,0
      +#
      +
      SML with daily values
      >D  
      +pin=0  
      +pout=0  
      +pi_d=0  
      +po_d=0  
      +hr=0  
      +; permanent midnight values  
      +p:pi_m=0  
      +p:po_m=0  
      +>B  
      +->sensor53 r  
      +>T  
      +; get total consumption and total feed  
      +pin=SML#Total_in  
      +pout=SML#Total_out  
      +>S  
      +; at midnight, save meter total values  
      +hr=hours  
      +if chg[hr]>0  
      +and hr==0  
      +then  
      +pi_m=pin  
      +po_m=pout  
      +svars  
      +endif  
      +; on teleperiod calculate current daily values from midnight  
      +if upsecs%tper==0  
      +then  
      +pi_d=pin-pi_m  
      +po_d=pout-po_m  
      +endif  
      +; show these values on WEB UI  
      +>W  
      +Tagesverbrauch: {m} %pi_d% kWh  
      +Tageseinspeisung: {m} %po_d% kWh    
      +; transmit these values with MQTT  
      +>J  
      +,"daily_consumption":%pi_d%,"daily_feed":%po_d%  
      +; meter definition  
      +>M 1  
      ++1,3,s,0,9600,SML  
      +1,77070100010800ff@1000,Total Consumed,KWh,Total_in,4  
      +1,77070100020800ff@1000,Total Delivered,KWh,Total_out,4  
      +1,77070100100700ff@1,Current Consumption,W,Power_curr,0  
      +1,77070100000009ff@#,Meter Number,,Meter_number,0  
      +#
      +

      Hiking DDS238-2 ZN/S3 4x (MODBus)~

      This is an example for 4 MODBus devices on the same bus (at different addresses).

      Wiring diagram

      94788926-930d3b00-03d4-11eb-8951-7b379c65f9ca

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,m,1,9600,Hiking,1,10,0103000c,0103000e,0303000c,0303000e,0403000c,0403000e,0503000c,0503000e
      +;---> two groups of registers for each device --> default 2 registers returned ---> 4 values per device
      +1,=h Contatore 1
      +1,010304UUuu@i0:10,C1_Voltage,V,C1Voltage,1
      +;---> decoder for the first registry returned for the first group
      +1,010304xxxxUUuu@i0:1000,C1_Current,A,C1Current,3
      +;---> decoder for the second registry returned for the first group
      +1,010304SSss@i1:1,C1_ActivePower,W,C1ActivePower,0
      +1,010304xxxxUUuu@i1:1,C1_ReactivePower,Var,C1ReactivePower,0
      +1,=h Contatore 3
      +1,030304UUuu@i2:10,C3_Voltage,V,C3Voltage,1
      +1,030304xxxxUUuu@i2:1000,C3_Current,A,C3Current,3
      +1,030304SSss@i3:1,C3_ActivePower,W,C3ActivePower,0
      +1,030304xxxxUUuu@i3:1,C3_ReactivePower,Var,C3ReactivePower,0
      +1,=h Contatore 4
      +1,040304UUuu@i4:10,C4_Voltage,V,C4Voltage,1
      +1,040304xxxxUUuu@i4:1000,C4_Current,A,C4Current,3
      +1,040304SSss@i5:1,C4_ActivePower,W,C4ActivePower,0
      +1,040304xxxxUUuu@i5:1,C4_ReactivePower,Var,C4ReactivePower,0
      +1,=h Contatore 5
      +1,050304UUuu@i6:10,C5_Voltage,V,C5Voltage,1
      +1,050304xxxxUUuu@i6:1000,C5_Current,A,C5Current,3
      +1,050304SSss@i7:1,C5_ActivePower,W,C5ActivePower,0
      +1,050304xxxxUUuu@i7:1,C5_ReactivePower,Var,C5ReactivePower,0
      +#
      +

      Holley DTZ541 (SML)~

      This script reads pretty much all given information. Make sure to enable info in the settings, otherwise you only get total / current consuption and total export.

      This script was used and tested on a WeMos D1 mini with an IR Head connected to the RX pin (3).

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,16,9600,SML
      +1,77070100600100ff@#,Server ID,,server_id,0
      +1,77070100020800ff@1000,Export (Total),kWh,export_total_kwh,4
      +1,77070100010800ff@1000,Consumption (Total),kWh,total_kwh,4
      +1,77070100100700ff@1,Consumption (Current),W,curr_w,0
      +1,77070100200700ff@1,Voltage L1,V,volt_p1,1
      +1,77070100340700ff@1,Voltage L2,V,volt_p2,1
      +1,77070100480700ff@1,Voltage L3,V,volt_p3,1
      +1,770701001f0700ff@1,Amperage L1,A,amp_p1,1
      +1,77070100330700ff@1,Amperage L2,A,amp_p2,1
      +1,77070100470700ff@1,Amperage L3,A,amp_p3,1
      +1,77070100510701ff@1,Phase angle U-L2/U-L1,deg,phase_angle_l2_l1,1
      +1,77070100510702ff@1,Phase angle U-L3/U-L1,deg,phase_angle_l3_l1,1
      +1,77070100510704ff@1,Phase angle I-L1/U-L1,deg,phase_angle_p1,1
      +1,7707010051070fff@1,Phase angle I-L2/U-L2,deg,phase_angle_p2,1
      +1,7707010051071aff@1,Phase angle I-L3/U-L3,deg,phase_angle_p3,1
      +1,770701000e0700ff@1,Frequency,Hz,freq,0
      +#
      +

      Holley DTZ541-ZDBA (SML)~

      This meter differatiates between day and night time consumption. The script is based on the DTZ541. Look above for more information.

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,16,9600,SML
      +1,77070100600100ff@#,Server ID,,server_id,0
      +1,77070100020800ff@1000,Export (Total),kWh,export_total_kwh,4
      +1,77070100010802ff@1000,Night (Total),kWh,total_night_kwh,4
      +1,77070100010801ff@1000,Day (Total),kwH,total_day_kwh,4
      +1,77070100100700ff@1,Consumption (Current),W,curr_w,0
      +1,77070100200700ff@1,Voltage L1,V,volt_p1,1
      +1,77070100340700ff@1,Voltage L2,V,volt_p2,1
      +1,77070100480700ff@1,Voltage L3,V,volt_p3,1
      +1,770701001f0700ff@1,Amperage L1,A,amp_p1,1
      +1,77070100330700ff@1,Amperage L2,A,amp_p2,1
      +1,77070100470700ff@1,Amperage L3,A,amp_p3,1
      +1,77070100510701ff@1,Phase angle U-L2/U-L1,deg,phase_angle_l2_l1,1
      +1,77070100510702ff@1,Phase angle U-L3/U-L1,deg,phase_angle_l3_l1,1
      +1,77070100510704ff@1,Phase angle I-L1/U-L1,deg,phase_angle_p1,1
      +1,7707010051070fff@1,Phase angle I-L2/U-L2,deg,phase_angle_p2,1
      +1,7707010051071aff@1,Phase angle I-L3/U-L3,deg,phase_angle_p3,1
      +1,770701000e0700ff@1,Frequency,Hz,freq,0
      +#
      +

      Holley EHZ541-BE (SML)~

      This Meter is sending negative values similiar to the DTZ541 model, you have to use the special option 1 (so1) as described in the 'special commands' section. Make sure to acquire the PIN from your energyprovider.

      After unlocking the meter, you can run the following script

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,16,9600,SML
      +1,77070100600100ff@#,Server ID,,server_id,0
      +1,77070100010800ff@1000,Consumption (Total),kWh,total_kwh,4
      +1,=so1,00010800,65,11,65,11,00100700
      +1,77070100100700ff@1,Consumption (Current),W,curr_w,0
      +#
      +

      Huawei SUN2000-10KTL (Modbus)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,m,0,9600,modbus,1,10,r01037D100004,01037D00,01037D08,01037D40,r01037D450003,r01037D48000A,01037D55,01037D57,01037D59,01037D6A,01037D72
      +1,010308SSss@i0:10,PV1 Voltage,V,PV1_Voltage,1
      +1,010308x2SSss@i0:100,PV1 Current,A,PV1_Current,2
      +1,010308x4SSss@i0:10,PV2 Voltage,V,PV2_Voltage,1
      +1,010308x6SSss@i0:100,PV2 Current,A,PV2_Current,2
      +1,010304UUuu@i1:1,State Code,SC,State_Code,0
      +1,010304UUuuUUuu@i2:1,Error Code,EC,Error_Code,0
      +1,010304SSssSSss@i3:1,Input Power,W,Input_Power,0
      +1,010306UUuu@i4:10,Phase 1 Voltage,V,Phase_1_Voltage,1
      +1,010306x2UUuu@i4:10,Phase 2 Voltage,V,Phase_2_Voltage,1
      +1,010306x4UUuu@i4:10,Phase 3 Voltage,V,Phase_3_Voltage,1
      +1,010314SSssSSss@i5:1000,Phase 1 Current,A,Phase_1_Current,2
      +1,010314x4SSssSSss@i5:1000,Phase 2 Current,A,Phase_2_Current,2
      +1,010314x8SSssSSss@i5:1000,Phase 3 Current,A,Phase_3_Current,2
      +1,010314x16SSssSSss@i5:1,Active Power,W,Active_Power,0
      +1,010304UUuu@i6:100,Frequency,Hz,Frequency,2
      +1,010304SSss@i7:10,Internal Temperature,c,Internal_Temperature,1
      +1,010304UUuu@i8:1,Status Code,SC,Status_Code,0
      +1,010304UUuuUUuu@i9:100,Total Yield,TY,Total_Yield,2
      +1,010304UUuuUUuu@i10:100,Daily Yield,DY,Daily_Yield,2
      +#
      +

      Iskra MT 174 (OBIS)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,o,0,300,STROM,1,100,2F3F210D0A
      +1,1-0:1.8.1*255(@1,Total Consumed,KWh,Total_in,3
      +1,1-0:2.8.1*255(@1,Total Delivered,KWh,Total_out,3
      +1,1-0:0.0.0*255(@#),Meter Number,,Meter_number,0
      +#
      +

      Iskra MT 175 (SML)~

      This meter needs a PIN to unlock the current power usage. You need to ask your provider.

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,16,9600,MT175
      +1,77070100010800ff@1000,E_in,kWh,E_in,1
      +1,77070100020800ff@1000,E_out,kWh,E_out,1
      +1,77070100100700ff@1,P,W,P,18
      +1,77070100240700ff@1,L1,W,L1,18
      +1,77070100380700ff@1,L2,W,L2,18
      +1,770701004C0700ff@1,L3,W,L3,18
      +1,77070100000009ff@#,Server_ID,,Server_ID,0
      +#
      +

      Iskra MT 681 (SML)~

      This is script for a two-direction meter (consumption and delivery) for the Isra MT 681, that is widely used in Germany. If you don't deliver energy, just delete the "Total Delivered" line. If the meter provides the consumption values for the 3 phases depends also on the configuration by your local energy provider.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,0,9600,MT681
      +1,77070100010800ff@1000,Total Consumed,KWh,Total_in,3
      +1,77070100100700ff@1,Current Consumption,W,Power_cur,0
      +1,77070100240700ff@1,Current Consumption P1,W,Power_p1,0
      +1,77070100380700ff@1,Current Consumption P2,W,Power_p2,0
      +1,770701004c0700ff@1,Current Consumption P3,W,Power_p3,0
      +1,77070100020800ff@1000,Total Delivered,KWh,Total_out,3
      +1,77070100000009ff@#,Service ID,,Meter_id,0|
      +#
      +

      Iskra eHZ-MT681-D4A51-K0p~

      2012 version of the Iskra MT 681 with slightly other OBIS codes for the power values.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,0,9600,MT681
      +1,77070100010800ff@1000,Gesamtverbrauch,KWh,Total_in,3
      +1,770701000f0700ff@1,Leistung,W,Power_cur,0
      +1,77070100150700ff@1,Leistung P1,W,Power_p1,0
      +1,77070100290700ff@1,Leistung P2,W,Power_p2,0
      +1,770701003d0700ff@1,Leistung P3,W,Power_p3,0
      +1,77070100020800ff@1000,Gesamteinspeisung,KWh,Total_out,3
      +1,77070100000009ff@#,Service ID,,Meter_id,0|
      +#
      +

      Iskra eHZ-MT681-D4A52-K0p~

      2023 Version Zweiwegezähler

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,0,9600,MT681
      +1,77070100010800ff@1000,Verbrauch,KWh,Total_in,4
      +1,77070100100700ff@1,Leistung,W,Power_cur,0
      +1,77070100020800ff@1000,Erzeugung,KWh,Total_out,4
      +1,77070100000009ff@#,Service ID,,Meter_id,0|
      +#
      +

      Itron (SML V1.04)~

      The Itron electrical meter is a German end-user meter installed by EnBW. You can read values using an IR Sensor. The following script shows the meter number and the consuption and the generation of a Photovoltaik generator.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,12,s,0,9600,ELZ
      +1,77070100600100ff@#,Zählernummer,,Wert,0
      +1,77070100010800ff@1000,Verbrauch,kWh,ELZ_PV_1.8.0,1
      +1,77070100020800ff@1000,Erzeugung,kWh,ELZ_PV_2.8.0,1
      +#
      +
      This script additionally reads the power in watts

      It has enhanced precision of 4 decimal places for the total consumption. Be sure to turn on the full precision at the meter using a flashlight (if you see inF=Off, hold for 5 seconds until you see inF=On)

      >D
      +>B
      +=>sensor53 r
      +;Set teleperiod to 20sec  
      +tper=10  
      +>M 1
      ++1,3,s,0,9600,Power
      +1,77070100600100ff@#,Zählernummer,,Meter_Number,0
      +1,77070100010800ff@1000,Verbrauch,kWh,Total_in,4
      +1,77070100100700ff@1,Leistung,W,Power_curr,0
      +1,77070100020800ff@1000,Erzeugung,kWh,Total_out,4
      +#
      +

      Janitza B23 (MODBus)~

      View script
      >D
      +>B  
      +->sensor53 r
      +>M 1  
      ++1,3,m,0,9600,Janitza,1,1,01034A38,01034A3A,01034A3C,01034A4C,01034A4E,01034A50,01034A72,01034A7A,01034A82  
      +1,010304ffffffff@i0:1,Voltage L1-N,V,Voltage_L1-N,2  
      +1,010304ffffffff@i1:1,Voltage L2-N,V,Voltage_L2-N,2  
      +1,010304ffffffff@i2:1,Voltage L3-N,V,Voltage_L3-N,2  
      +1,010304ffffffff@i3:1,Real power L1-N,W,Real_power_L1-N,2  
      +1,010304ffffffff@i4:1,Real power L2-N,W,Real_power_L2-N,2  
      +1,010304ffffffff@i5:1,Real power L3-N,W,Real_power_L3-N,2  
      +1,010304ffffffff@i6:1,Real energy L3,Wh,Real_energy_L3,2  
      +1,010304ffffffff@i7:1,Real energy L3-consumed,Wh,Real_energy_L3_consumed,2  
      +1,010304ffffffff@i8:1,Real energy L3-delivered,Wh,Real_energy_L3_delivered,2   
      +#
      +

      JANZ C3801 (MODBus)~

      This is an example for one of the many quite similar smart meters implemented in Portugal, by EDP Distribuição S.A. May be valid for many more models, as stated.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,14,m,1,9600,EB,5,50,0104006C,01040079,0104007A,0104007F,01040026,01040027,01040028,0104000B,01040084
      +1,=hVALORES TÉCNICOS
      +1,010404UUuuxxxx@i0:10,Tensão,V,Voltage_P1,17
      +1,010404xxxxUUuu@i0:10,Corrente,A,Current_P1,17
      +1,010408UUuuUUuuxxxxxxxxxxxx@i1:1,Potência ativa,W,Power_P1,16
      +1,010406xxxxxxxxUUuu@i2:1000,Fator de potência,pu,PFactor_P1,19
      +1,01040aUUuuxxxx@i3:10,Frequência,Hz,Frequency_P1,17
      +1,=h&#8205;
      +1,=hTOTALIZADORES DE ENERGIA
      +1,010408UUuuUUuuxxxxxxxxxxxx@i4:1000,Vazio (1),kWh,Energy_P1_R1,17
      +1,010408UUuuUUuuxxxxxxxxxxxx@i5:1000,Ponta (2),kWh,Energy_P1_R2,17
      +1,010408UUuuUUuuxxxxxxxxxxxx@i6:1000,Cheia (3),kWh,Energy_P1_R3,17
      +1,=h&#8205;
      +1,=hESTADOS
      +1,010406uuxxxxxxxx@i7:1,Tarifa,,Tariff_P1,16
      +1,010406uuxxxxxxxx@i8:1,DCP,,DCP_P1,16
      +#
      +

      KAIFA MB310H4BDE~

      By default, the KAIFA MB310H4BDE will only deliver the Total_in and Total_out values (without decimals). In order to get precise values, and especially in order to receive Power_curr, you have to login to configure it: Press the button besides the display. When asked for pin, enter it with a flashlight or by pressing the same button repeatedly. (If you don't have a PIN, get it from your grid operator.) If that was successful, you will see accurate values in the meters display already. To activate them on the SML interface, press the button repeatedly for 13 times, until "INF OFF" is displayed. Now press and hold the button, until it switches to INF ON. After that, you should receive all values.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,0,9600,Haus
      +1,77070100010800ff@1000,Zaehlerstand In,KWh,Total_in,2
      +1,77070100020800ff@1000,Zaehlerstand Out,KWh,Total_out,2
      +1,77070100100700ff@1,Leistung-akt.,W,Power_curr,0
      +1,77070100600100ff@#,Server-ID,,Meter_Number,0
      +#
      +

      Kamstrup Multical 4xx / 6xx / 8xx~

      Those meters provide several registers via optical interface called D0 interface. Baudrate is only 1200 thus reading multiple registers simultaneously takes some time (e.g. one: ~300ms, six ~750ms). Size of response varies depending on register type and needs to be considered when reading more than one with a single telegram. Analyze length with sensor53 d1. Responses have format of 3F10<rg><response><rg><response>. So asking for 0x003C and 0x0056 with telegram 3F10 02 003C 0056 could be responded with 3F10 003C xxXXxxXXxxXXxx 0056 xxXXxxXXxx.

      Default buffer size suffices for four values at once. The following example reads seven values at once so please #define SML_BSIZ 90. SML allows only one date value per meter at the moment.

      View script (seven at once, needs #define SML_BSIZ 90)
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,kN2,0,1200,KSMC403,1,10,3F1007003C005600570059004A0044007B
      +1,3F10003Ckstr@i0:1000,Wärmemenge,MWh,HeatEnergyE1,3
      +1,3F10x08xx0056kstr@i0:100,Vorlauftemp.,°C,PreTemp,2
      +1,3F10x15xx0057kstr@i0:100,Rücklauftemp.,°C,AftTemp,2
      +1,3F10x22xx0059kstr@i0:100,Temp.diff.,°C,DifTemp,2
      +1,3F10x29xx004Akstr@i0:1,Fließgeschw.,l/h,Flow,0
      +1,3F10x36xx0044kstr@i0:100,Volumenstrom,m³,Volume,2
      +1,3F10x45xx007Bkstr@#,max. Fluss am,,MaxFlowDate,0
      +#
      +
      View script (same as above with two telegrams and default SML_BSIZ)
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,kN2,0,1200,KSMC403,1,5,3F1004003C005600570059,3F1003004A0044007B
      +1,3F10003Ckstr@i0:1000,Wärmemenge,MWh,HeatEnergyE1,3
      +1,3F10x08xx0056kstr@i0:100,Vorlauftemp.,°C,PreTemp,2
      +1,3F10x15xx0057kstr@i0:100,Rücklauftemp.,°C,AftTemp,2
      +1,3F10x22xx0059kstr@i0:100,Temp.diff.,°C,DifTemp,2
      +1,3F10004Akstr@i1:1,Fließgeschw.,l/h,Flow,0
      +1,3F10x06xx0044kstr@i1:100,Volumenstrom,m³,Volume,2
      +1,3F10x15xx007Bkstr@#,max. Fluss am,,MaxFlowDate,0
      +#
      +

      Landis + Gyr E220 (SML)~

      For read-out of "current power" the advanced data set has to be enabled in user menu

      View script
      >D
      +>B
      +=>sensor53 r
      +;Set teleperiod to 20sec  
      +tper=10  
      +>M 1
      ++1,3,s,0,9600,Power
      +1,77070100600100ff@#,Server-ID,,Meter_Number,0
      +1,77070100010800ff@1000,Verbrauch,kWh,Total_in,4
      +1,77070100100700ff@1,Leistung-akt.,W,Power_curr,0
      +#
      +

      Landis + Gyr E320 (SML)~

      For read-out of "Current power" the advanced data set has to be enabled in user menu

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,20,9600,E320
      +1,77070100020800ff@1000,Total Delivered,KWh,Total_out,3
      +1,77070100010800ff@1000,Total Consumed,KWh,Total_in,3
      +1,77070100100700ff@1,Current power,W,Power_in,3
      +1,77070100600100ff@#,Server-ID,,Meter_Number,0    
      +#
      +

      Landis + Gyr ZMB120 T213CS (OBIS)~

      This meter may need a PIN to unlock the current power usage - ask your provider.

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,o,0,300,STROM,1,10,2F3F210D0A,063030300D0A
      +1,0(@1,Zählernummer,,Meter_number,0  
      +1,=h===================  
      +1,8.0(@1,Total T1 & T2,KWh,Total,2  
      +1,8.1(@1,T1 aktuell,KWh,Total_T1,2  
      +1,8.2(@1,T2 aktuell,KWh,Total_T2,2  
      +1,=h===================  
      +1,8.1.1(@1,T1 letzte Saison,KWh,Total_T1-1,2   
      +1,8.2.1(@1,T2 letzte Saison,KWh,Total_T2-1,2   
      +1,8.1.2(@1,T1 vorletzte Saison,KWh,Total_T1-2,2
      +1,8.2.2(@1,T2 vorletzte Saison,KWh,Total_T2-2,2     
      +#
      +

      Landis + Gyr ZMR120AReS2R2sfCS (OBIS)~

      Example: Changing the baud rate during operation.

      View script
      >D  
      +;Var Power consumption total HT+NT  
      +v1=0  
      +;HT Main electricity tariff consumption total   
      +v2=0  
      +;NT Night electricity tariff consumption total  
      +v3=0  
      +; Energie L1+L2+L3  
      +v4=0  
      +;recent Energie L1  
      +v5=0  
      +;recent Energie L2  
      +v6=0  
      +;recent Energie L3  
      +v7=0  
      +
      +;Var minute   
      +min=0  
      +;Var hour  
      +hr=0  
      +;Var begin of the month 01.xx.20xx 0:00 Uhr  
      +md=0  
      +;Var begin of the year 01.01. 0:00 Uhr  
      +yr=0  
      +;Var for counter see >F=ms  
      +scnt=0  
      +;Var for baudrate changing
      +res=0  
      +
      +;Permanent Var Meter1 0:00   
      +p:sm=0  
      +p:HT_sm=0  
      +p:NT_sm=0  
      +;Var for daily =0  
      +sd=0  
      +HT_sd=0  
      +NT_sd=0  
      +;Permanent Var for month begin  
      +p:sma=0  
      +p:HT_sma=0  
      +p:NT_sma=0  
      +;Var for monthly =0  
      +smn=0  
      +HT_smn=0  
      +NT_smn=0  
      +;Permanent Var for year begin  
      +p:sya=0  
      +p:HT_sya=0  
      +p:NT_sya=0  
      +;Var for yearly =0  
      +syn=0  
      +HT_syn=0  
      +NT_syn=0  
      +
      +;Fill vars with content on teleperiod    
      +>T  
      +v1=#Total_in  
      +v2=#HT_Total_in  
      +v3=#NT_Total_in  
      +v4=#kW_L1+L2+L3  
      +v5=#kw_L1  
      +v6=#kw_L2  
      +v7=#kw_L3  
      +
      +>B  
      +;Restart driver  
      +->sensor53 r  
      +;Set teleperiod to 20sec  
      +tper=20  
      +
      +>F  
      +; count 100ms   
      +scnt+=1  
      +switch scnt  
      +case 6  
      +;set sml driver to 300 baud and send /?! as HEX to trigger the Meter   
      +res=sml(1 0 300)  
      +res=sml(1 1 "2F3F210D0A")  
      +
      +;1800ms later \> Ack and ask for switching to 9600 baud  
      +case 18  
      +res=sml(1 1 "063035300D0A")  
      +
      +;2000ms later \> Switching sml driver to 9600 baud    
      +case 20  
      +res=sml(1 0 9600)  
      +
      +;Restart sequence after 50x100ms    
      +case 50  
      +; 5000ms later \> restart sequence    
      +scnt=0  
      +ends  
      +
      +>S  
      +;daily usage  
      +hr=hours  
      +if chg[hr]>0  
      +and hr==0  
      +and v1>0  
      +then  
      +sm=v1  
      +HT_sm=v2  
      +NT_sm=v3  
      +svars  
      +endif  
      +
      +if upsecs%tper==0{  
      +sd=v1-sm  
      +HT_sd=v2-HT_sm  
      +NT_sd=v3-NT_sm  
      +}  
      +
      +;Monthly usage  
      +md=day  
      +if chg[md]>0  
      +and md==1  
      +and v1>0  
      +then  
      +sma=v1  
      +HT_sma=v2  
      +NT_sma=v3  
      +svars  
      +endif  
      +
      +if upsecs%tper==0{  
      +smn=v1-sma  
      +HT_smn=v2-HT_sma  
      +NT_smn=v3-NT_sma  
      +}  
      +
      +;Yearly usage  
      +yr=year  
      +if chg[yr]>0  
      +and v1>0  
      +then  
      +sya=v1  
      +HT_sya=v2  
      +NT_sya=v3  
      +svars  
      +endif  
      +
      +if upsecs%tper==0{  
      +syn=v1-sya  
      +HT_syn=v2-HT_sya  
      +NT_syn=v3-NT_sya  
      +
      +; Json payload \> send on teleperiod  
      +>J  
      +,"Strom_Vb_Tag":%3sd%  
      +,"HT_Strom_Vb_Tag":%3HT_sd%  
      +,"NT_Strom_Vb_Tag":%3NT_sd%  
      +,"Strom_Vb_M":%1smn%  
      +,"HT_Strom_Vb_M":%1HT_smn%  
      +,"NT_Strom_Vb_M":%1NT_smn%  
      +,"Strom_Vb_Jahr":%0syn%  
      +,"HT_Strom_Vb_Jahr":%0HT_syn%  
      +,"NT_Strom_Vb_Jahr":%0NT_syn%  
      +,"Strom_0:00 _Uhr":%1sm%  
      +,"HT_Strom_0:00 _Uhr":%1HT_sm%  
      +,"NT_Strom_0:00 _Uhr":%1NT_sm%  
      +,"Strom_Ma":%3sma%  
      +,"HT_Strom_Ma":%3HT_sma%  
      +,"NT_Strom_Ma":%3NT_sma%  
      +,"Strom_Ja":%3sya%  
      +,"HT_Strom_Ja":%3HT_sya%  
      +,"NT_Strom_Ja":%3NT_sya%  
      +
      +;Webdisplay stuff  
      +>W  
      +
      +0:00 Uhr Σ HT+NT: {m} %0sm% KWh  
      +HT: {m} %0HT_sm% KWh  
      +NT: {m} %0NT_sm% KWh  
      +
      +Monatsanfang: {m} %1sma% KWh  
      +HT: {m} %1HT_sma% KWh  
      +NT: {m} %1NT_sma% KWh  
      +
      +Jahresanfang: {m} %0sya% KWh  
      +HT: {m} %0HT_sya% KWh  
      +NT: {m} %0NT_sya% KWh  
      +.............................  
      +Tagesverbrauch: {m} %1sd% KWh  
      +HT: {m} %1HT_sd% KWh  
      +NT: {m} %1NT_sd% KWh  
      +
      +Monatsverbrauch: {m} %0smn% KWh  
      +HT: {m} %0HT_smn% KWh  
      +NT: {m} %0NT_smn% KWh  
      +    -  
      +Jahresverbrauch: {m} %0syn% KWh  
      +HT: {m} %0HT_syn% KWh  
      +0:00 Uhr Σ HT+NT: {m} %0sm% KWh  
      +HT: {m} %0HT_sm% KWh  
      +NT: {m} %0NT_sm% KWh  
      +
      +Monatsanfang: {m} %1sma% KWh  
      +HT: {m} %1HT_sma% KWh  
      +NT: {m} %1NT_sma% KWh  
      +
      +Jahresanfang: {m} %0sya% KWh  
      +HT: {m} %0HT_sya% KWh  
      +NT: {m} %0NT_sya% KWh  
      +.............................  
      +Tagesverbrauch: {m} %1sd% KWh  
      +HT: {m} %1HT_sd% KWh  
      +NT: {m} %1NT_sd% KWh  
      +
      +Monatsverbrauch: {m} %0smn% KWh  
      +HT: {m} %0HT_smn% KWh  
      +NT: {m} %0NT_smn% KWh  
      +    -  
      +Jahresverbrauch: {m} %0syn% KWh  
      +HT: {m} %0HT_syn% KWh  
      +NT: {m} %0NT_syn% KWhNT: {m} %0NT_syn% KWh  
      +
      +>M 1  
      ++1,3,o,0,9600,,1  
      +1,0.0.1(@1,Zählernummer,,Meter_number,0  
      +1,0.9.1(@#),Zeitstempel,Uhr,time-stamp,0  
      +1,=h===================  
      +1,1.8.0(@1,HT+NT Zählerstand,KWh,Total_in,3  
      +1,1.8.1(@1,HT,KWh,HT_Total_in,3  
      +1,1.8.2(@1,NT,KWh,NT_Total_in,3  
      +1,=h===================  
      +1,36.7.0(@1,Power_L1,kW,kW_L1,2  
      +1,56.7.0(@1,Power_L2,kW,kW_L2,2  
      +1,76.7.0(@1,Power_L3,kW,kW_L3,2  
      +1,16.7.0(@1,Σ_L1+L2+L3,kW,kW_L1+L2+L3,2  
      +1,=h===================  
      +1,31.7.0(@1,Strom_L1,A,I_L1,2  
      +1,51.7.0(@1,Strom_L2,A,I_L2,2  
      +1,71.7.0(@1,Strom_L3,A,I_L3,2  
      +#
      +

      2 * Landis+Gyr E450 (encrypted)~

      View script
      >D
      +>B
      +smlj=0
      +=>sensor53 r
      +>R
      +smlj=0
      +>S
      +if upsecs>22
      +then
      +smlj|=1
      +endif
      +>M 2
      ++1,17,r,0,2400,Heizung
      +1,=so3,512
      +1,=so4,GUEK
      +1,pm(1.8.0)@1000,kWh_IN,kWh,kWh_IN,3;Wirkenergie Lieferung (+A)
      +1,pm(1.8.1)@1000,kWh_IN_T1,kWh,kWh_IN_T1,3;Wirkenergie Lieferung (+A) Tarif 1
      +1,pm(1.8.2)@1000,kWh_IN_T2,kWh,kWh_IN_T2,3;Wirkenergie Lieferung (+A) Tarif 2
      +1,pm(1.7.0)@1000,kW_IN,kW,kW_IN,3;Momentane Wirkleistung Lieferung (+A)
      +;1,pm(2.8.0)@1000,kWh_OUT,kWh,kWh_OUT,3;Wirkenergie Bezug (-A)
      +;1,pm(2.8.1)@1000,kWh_OUT_T1,kWh,kWh_OUT_T1,3;Wirkenergie Bezug (-A) Tarif 1
      +;1,pm(2.8.2)@1000,kWh_OUT_T2,kWh,kWh_OUT_T2,3;Wirkenergie Bezug (-A) Tarif 2
      +;1,pm(.2.7.0)@1000,kW_OUT,kW,kW_OUT,3;Momentane Wirkleistung Bezug (-A)
      +1,pm(3.8.0)@1000,kvarh_IN,kvarh,kvarh_IN,3;Blindenergie Lieferung (+R)
      +1,pm(.3.8.1)@1000,kvarh_IN_T1,kvarh,kvarh_IN_T1,3;Blindenergie Lieferung (+R) Tarif 1
      +1,pm(.3.8.2)@1000,kvarh_IN_T2,kvarh,kvarh_IN_T2,3;Blindenergie Lieferung (+R) Tarif 2
      +1,pm(.3.7.0)@1000,kvar_IN,kvar,kvar_IN,3;Momentane Blindleistung Lieferung (+R)
      +1,pm(4.8.0)@1000,kvarh_OUT,kvarh,kvarh_OUT,3;Blindenergie Bezug (-R)
      +1,pm(.4.8.1)@1000,kvarh_OUT_T1,kvarh,kvarh_OUT_T1,3;Blindenergie Bezug (-R) Tarif 1
      +1,pm(.4.8.2)@1000,kvarh_OUT_T2,kvarh,kvarh_OUT_T2,3;Blindenergie Bezug (-R) Tarif 2
      +1,pm(.4.7.0)@1000,kvar_OUT,kvar,kvar_OUT,3;Momentane Blindleistung Bezug (-R)
      +
      ++2,16,r,0,2400,Haus
      +2,=so3,512
      +2,=so4,GUEK
      +2,pm(1.8.0)@1000,kWh_IN,kWh,kWh_IN,3;Wirkenergie Lieferung (+A)
      +2,pm(1.8.1)@1000,kWh_IN_T1,kWh,kWh_IN_T1,3;Wirkenergie Lieferung (+A) Tarif 1
      +2,pm(1.8.2)@1000,kWh_IN_T2,kWh,kWh_IN_T2,3;Wirkenergie Lieferung (+A) Tarif 2
      +2,pm(1.7.0)@1000,kW_IN,kW,kW_IN,3;Momentane Wirkleistung Lieferung (+A)
      +;2,pm(2.8.0)@1000,kWh_OUT,kWh,kWh_OUT,3;Wirkenergie Bezug (-A)
      +;2,pm(2.8.1)@1000,kWh_OUT_T1,kWh,kWh_OUT_T1,3;Wirkenergie Bezug (-A) Tarif 1
      +;2,pm(2.8.2)@1000,kWh_OUT_T2,kWh,kWh_OUT_T2,3;Wirkenergie Bezug (-A) Tarif 2
      +;2,pm(2.7.0)@1000,kW_OUT,kW,kW_OUT,3;Momentane Wirkleistung Bezug (-A)
      +2,pm(.3.8.0)@1000,kvarh_IN,kvarh,kvarh_IN,3;Blindenergie Lieferung (+R)
      +2,pm(3.8.1)@1000,kvarh_IN_T1,kvarh,kvarh_IN_T1,3;Blindenergie Lieferung (+R) Tarif 1
      +2,pm(3.8.2)@1000,kvarh_IN_T2,kvarh,kvarh_IN_T2,3;Blindenergie Lieferung (+R) Tarif 2
      +2,pm(3.7.0)@1000,kvar_IN,kvar,kvar_IN,3;Momentane Blindleistung Lieferung (+R)
      +2,pm(4.8.0)@1000,kvarh_OUT,kvarh,kvarh_OUT,3;Blindenergie Bezug (-R)
      +2,pm(4.8.1)@1000,kvarh_OUT_T1,kvarh,kvarh_OUT_T1,3;Blindenergie Bezug (-R) Tarif 1
      +2,pm(4.8.2)@1000,kvarh_OUT_T2,kvarh,kvarh_OUT_T2,3;Blindenergie Bezug (-R) Tarif 2
      +2,pm(4.7.0)@1000,kvar_OUT,kvar,kvar_OUT,3;Momentane Blindleistung Bezug (-R)
      +#
      +

      Landis+Gyr E650 (OBIS)~

      The script switches to a higher baud rate as the data set is pretty big and takes minutes to complete at 300 baud. The usable speed for my setup/hardware was 4800 baud.

      The script requires tasmota version 12+ so it supports variables.

      Switching to different baud rates requires changing the ack sequence 06303x300D0A, the baud rate in 2 places 4800->x and the case 200 (20x100ms) so the cycle can complete before restarting.

      View script
      >D
      +; Script needs v12+
      +res=0
      +scnt=0
      +>F
      +;F section is executed every 100ms
      +scnt+=1
      +switch scnt
      +case 6
      +;set sml driver to 300 baud and send /?! as HEX to trigger the Meter
      +res=sml(1 0 300)
      +res=sml(1 1 "2F3F210D0A")
      +;1800ms later \> Send ACK and ask for switching to 4800 baud
      +case 18
      +res=sml(1 1 "063034300D0A")
      +res=sml(1 0 4800)
      +case 200
      +; 20s later \> restart sequence
      +scnt=0
      +ends      
      +>M 1
      ++1,3,o,0,4800,data,1
      +
      +1,1-1:1.8.0(@1,energy_import,KWh,1-8-0,1
      +1,1-1:2.8.0(@1,energy_export,KWh,2-8-0,1
      +1,1-1:36.7.0(@1,power_L1,KWh,36-7-0,2
      +1,1-1:56.7.0(@1,power_L2,KWh,56-7-0,2
      +1,1-1:76.7.0(@1,power_L3,KWh,76-7-0,2
      +1,1-1:31.7.0(@1,current_L1,A,31-7-0,2
      +1,1-1:51.7.0(@1,current_L2,A,51-7-0,2
      +1,1-1:71.7.0(@1,current_L3,A,71-7-0,2
      +1,1-1:16.7.0(@1,power_total,W,16-7-0,2
      +#
      +

      Logarex LK11BL (OBIS)~

      This script keeps optical communication on the initial 300 baud speed and reject to switch any other speed smart meter requests.
      +
      +In my case, smart meter wanted to switch to 9600 baud but I felt it is better to follow the 'keep it simple' principle at the script's level and use 300 baud through the whole communication.
      +
      +If you know the meaning of the below unnamed OBIS codes please update the script.
      +
      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,o,0,300,zm,1,100,2F3F210D0A,060000D0A
      +
      +1,15.8.0(@1,total,kWh,total,3
      +1,0.0.0(@1,serial,,serial,0
      +1,F.F(@1,F.F,,F.F,0
      +1,C.8.0(@1,C.8.0,,C.8.0,0
      +1,0.2.0(@#),ver,,ver,3
      +1,0.3.0(@1,0.3.0,imp/kWh,0.3.0,0
      +1,.8.1(@1,.8.1,,.8.1,0
      +1,C.7.1(@1,C.7.1,,C.7.1,0
      +1,C.2.1(@1,C.2.1,,C.2.1,0
      +1,C.2.9(@1,C.2.9,,C.2.9,0
      +#
      +

      Logarex LK13BE (OBIS)~

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,o,0,9600,LK13BE,13,30,2F3F210D0A,063035310D0A
      +
      +1,1-0:1.8.0*255(@1,Gesamtverbrauch,KWh,total,4
      +1,1-0:1.8.0*96(@1,Verbrauch 1 Tag,KWh,total_1d,4
      +1,1-0:1.8.0*97(@1,Verbrauch 7 Tage,KWh,total_7d,4
      +1,1-0:1.8.0*98(@1,Verbrauch 30 Tage,KWh,total_30d,4
      +1,1-0:1.8.0*99(@1,Verbrauch 365 Tage,KWh,total_365d,4
      +1,1-0:16.7.0*255(@1,Verbrauch aktuell,W,power,20
      +#
      +

      Logarex LK13BE803039~

      The Logarex LK13BE803039 does publish the data automatically. Do not poll this can lead to missreadings

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,o,0,9600,LK13BE
      +
      +1,1-0:1.8.0*255(@1,Gesamtverbrauch,KWh,total,4
      +1,1-0:1.8.0*96(@1,Verbrauch 1 Tag,KWh,total_1d,4
      +1,1-0:1.8.0*97(@1,Verbrauch 7 Tage,KWh,total_7d,4
      +1,1-0:1.8.0*98(@1,Verbrauch 30 Tage,KWh,total_30d,4
      +1,1-0:1.8.0*99(@1,Verbrauch 365 Tage,KWh,total_365d,4
      +1,1-0:16.7.0*255(@1,Verbrauch aktuell,W,current,20
      +1,1-0:2.8.0*255(@1,Gesamteinspeisung,KWh,total_out,4
      +#
      +

      Logarex LK13BE803319 (OBIS)~

      setupline for the HichiIR WiFi module (GPIO1: send, GPIO3: receive)

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      +; setupline
      ++1,3,o,0,9600,LK13BE,1,30,2F3F210D0A,063035310D0A
      +; available without PIN, remove the semicolon the enable some optional values
      +; only one string (@#) can be decoded per meter
      +1,1-0:96.1.0*255(@#),Zählernummer,,id,0
      +;1,1-0:0.2.0*255(@#),Firmware,,fw,0
      +1,1-0:1.8.0*255(@1,Gesamtverbrauch,KWh,total,4
      +; available with PIN
      +1,1-0:1.8.0*96(@1,Verbrauch 1 Tag,KWh,total_1d,4
      +1,1-0:1.8.0*97(@1,Verbrauch 7 Tage,KWh,total_7d,4
      +1,1-0:1.8.0*98(@1,Verbrauch 30 Tage,KWh,total_30d,4
      +1,1-0:1.8.0*99(@1,Verbrauch 365 Tage,KWh,total_365d,4
      +1,1-0:1.8.0*100(@1,Verbrauch ab reset,KWh,total_reset,4
      +1,1-0:16.7.0*255(@1,Verbrauch aktuell,W,power,20
      +; available with PIN and full dataset enabled
      +1,1-0:32.7.0*255(@1,Spannung L1,V,voltage_l1,1
      +1,1-0:52.7.0*255(@1,Spannung L2,V,voltage_l2,1
      +1,1-0:72.7.0*255(@1,Spannung L3,V,voltage_l3,1
      +1,1-0:31.7.0*255(@1,Strom L1,A, amperage_l1,1
      +1,1-0:51.7.0*255(@1,Strom L2,A, amperage_l2,1
      +1,1-0:71.7.0*255(@1,Strom L3,A, amperage_l3,1
      +1,1-0:81.7.1*255(@1,UL2 zu UL1,deg,angle_ul2_ul1,0
      +1,1-0:81.7.2*255(@1,UL3 zu UL1,deg,angle_ul3_ul1,0
      +1,1-0:81.7.4*255(@1,IL1 zu UL1,deg,angle_il1_ul1,0
      +1,1-0:81.7.15*255(@1,IL2 zu UL2,deg,angle_il2_ul2,0
      +1,1-0:81.7.26*255(@1,IL3 zu UL3,deg,angle_il2_ul3,0
      +1,1-0:14.7.0*255(@1,Frequenz,Hz,frequency,1
      +#
      +

      Logarex LK13BE (SML) (LK13BE904639)~

      This meter does not provide detailed information regarding phase etc.

      View script
      >D
      +>B
      +=>sensor53 r
      +; Monitor Sensor at GPIO25
      +=>sensor53 l25
      +>M 1
      ++1,3,s,0,9600,LK13BE,1,10,2F3F210D0A,063035310D0A
      +
      +1,77070100010800ff@1000,Energie gesamt,kWh,energy_sum,3 
      +1,77070100010801ff@1000,Energie Tarif 1,kWh,energy_tarif1,3 
      +1,77070100010802ff@1000,Energie Tarif 2,kWh,energy_tarif2,3
      +1,77070100020800ff@1000,Einspeisung,kWh,energy_supply,3
      +1,=h --------------
      +1,77070100100700ff@1,Leistung,W,power,16
      +1,=h --------------
      +1,77070100600100ff@#,Server ID,,meter_number,0
      +#
      +

      Logarex LK13BE (SML) (e.g. LK13BE6067x9)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,0,9600,LK13BE,1,10,2F3F210D0A,063035310D0A
      +
      +1,77070100010800ff@1000,Gesamt kWh bezogen,kWh,Power_total_in,1
      +1,77070100020800ff@1000,Gesamt kWh geliefert,kWh,Power_total_out,1
      +1,77070100100700ff@1,Verbrauch aktuell,W,Power_curr,0
      +1,77070100240700ff@1,Power L1,W,Power_L1_curr,0
      +1,77070100380700ff@1,Power L2,W,Power_L2_curr,0
      +1,770701004C0700ff@1,Power L3,W,Power_L3_curr,0
      +1,77070100200700ff@1,Voltage L1,V,Volt_L1_curr,1
      +1,77070100340700ff@1,Voltage L2,V,Volt_L2_curr,1
      +1,77070100480700ff@1,Voltage L3,V,Volt_L3_curr,1
      +1,770701001f0700ff@1,Amperage L1,A,Amperage_L1_curr,2
      +1,77070100330700ff@1,Amperage L2,A,Amperage_L2_curr,2
      +1,77070100470700ff@1,Amperage L3,A,Amperage_L3_curr,2
      +1,770701000e0700ff@1,Frequency,Hz,HZ,2
      +1,77070100510704ff@1,Phaseangle I-L1/U-L1,deg,phase_angle_p1,1 
      +1,7707010051070fff@1,Phaseangle I-L2/I-L2,deg,phase_angle_p2,1  
      +1,7707010051071aff@1,Phaseangle I-L3/I-L3,deg,phase_angle_p3,1 
      +1,77070100510701ff@1,Phase angle U-L2/U-L1,deg,phase_angle_l2_l1,1
      +1,77070100510702ff@1,Phase angle U-L3/U-L1,deg,phase_angle_l3_l1,1
      +#
      +

      Norax 3D+ (SML)~

      This script gives also the wattage per phase. Make sure to get the PIN from your grid operator! Tested on a WeMos D1 mini with an IR Head from https://agalakhov.github.io/ir-interface connected to the RX pin (3). The meter also outputs the phase angles, but i left them out since i do not need them. You can easily find additional values by activating the debug mode ("sensor53 d1" for the first meter, switch off after a few seconds with "sensor53 d0").

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,1,9600,SML
      +1,77070100010800ff@1000,Total consumption,KWh,Total_in,4
      +1,77070100020800ff@1000,Total Feed,KWh,Total_out,4
      +1,77070100100700ff@1,Current consumption,W,Power_curr,0
      +1,77070100200700ff@1,Voltage L1,V,Volt_p1,1
      +1,77070100340700ff@1,Voltage L2,V,Volt_p2,1
      +1,77070100480700ff@1,Voltage L3,V,Volt_p3,1
      +1,770701001f0700ff@1,Amperage L1,A,Amperage_p1,1
      +1,77070100330700ff@1,Amperage L2,A,Amperage_p2,1
      +1,77070100470700ff@1,Amperage L3,A,Amperage_p3,1
      +1,77070100240700ff@1,Current consumption L1,W,Power_curr_p1,0
      +1,77070100380700ff@1,Current consumption L2,W,Power_curr_p2,0
      +1,770701004c0700ff@1,Current consumption L3,W,Power_curr_p3,0
      +1,770701000e0700ff@1,Frequency,Hz,frequency,0
      +#
      +

      PAFAL 20EC3gr~

      Documentation for this Counter is very small. This informations were collected across the internet.

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,o,0,300,PAFAL,1,30,2F3F210D0A,063030300D0A
      +1,1.8.1*00(@1),Gesamtverbrauch_HT,kWh,Total_IN,2
      +1,1.8.2*00(@1),Gesamtverbrauch_NT,kWh,Total_IN,2
      +1,2.8.0*00(@1),Einspeisung,kWh,Total_OUT,2
      +#
      +
      Dump of the script
      15:48:40.855 : �H/PAF5EC3gr00006
      +15:48:45.643 : �0.0.0(serialnumber)
      +15:48:46.260 : 0.0.1(PAF)
      +15:48:46.768 : F.F(00)
      +15:48:47.405 : 0.2.0(1.27)
      +15:48:48.314 : 1.8.1*00(071354.27)
      +15:48:49.228 : 1.8.2*00(070726.91)
      +15:48:50.149 : 2.8.0*00(013640.33)
      +15:48:52.730 : C.2.1(000000000000)(                                                )
      +15:48:53.542 : 0.2.2(:::::G11)!
      +

      Peacefair PZEM004TV30 (MODBUS)~

      PZEM004T V30 multiple meters on Modbus

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,m,0,9600,ENERGY,1,1,02040000,02040001,02040003,02040005,02040007,02040008,03040000,03040001,03040003,03040005,03040007,03040008,05040000,05040001,05040003,05040005,05040007,05040008
      +1,=h<hr/>Sensor-2
      +1,020404UUuuxxxxxxxx@i0:10,Voltage,V,Sensor-1-V,2
      +1,020404UUuuUUuusxxxx@i1:1000,Current,A,Sensor-1-A,2
      +1,020404UUuuUUuusxxxx@i2:10,Power,W,Sensor-1-W,2
      +1,020404UUuuUUuusxxxx@i3:1000,Energy,kWh,Sensor-1-kWh,4
      +1,020404UUuuxxxxxxxx@i4:10,Frequency,Hz,Sensor-1-hz,2
      +1,020404UUuuxxxxxxxx@i5:100,Power Factor,PF,Sensor-1-PF,2
      +1,=h<hr/>Sensor-3
      +1,030404UUuuxxxxxxxx@i6:10,Voltage,V,Sensor-2-V,2
      +1,030404UUuuUUuusxxxx@i7:1000,Current,A,Sensor-2-A,2
      +1,030404UUuuUUuusxxxx@i8:10,Power,W,Sensor-2-W,2
      +1,030404UUuuUUuusxxxx@i9:1000,Energy,kWh,Sensor-2-kWh,4
      +1,030404UUuuxxxxxxxx@i10:10,Frequency,Hz,Sensor-2-hz,2
      +1,030404UUuuxxxxxxxx@i11:100,Power Factor,PF,Sensor-2-PF,2
      +1,=h<hr/>Sensor-5
      +1,050404UUuuxxxxxxxx@i12:10,Voltage,V,Sensor-05-V,2
      +1,050404UUuuUUuusxxxx@i13:1000,Current,A,Sensor-05-A,2
      +1,050404UUuuUUuusxxxx@i14:10,Power,W,Sensor-05-W,2
      +1,050404UUuuUUuusxxxx@i15:1000,Energy,kWh,Sensor-05-kWh,4
      +1,050404UUuuxxxxxxxx@i16:10,Frequency,Hz,Sensor-05-hz,2
      +1,050404UUuuxxxxxxxx@i17:100,Power Factor,PF,Sensor-05-PF,2
      +
      +#
      +

      Resol Deltasol BS Plus (VBus)~

      This is a controller for standard solar thermal systems equipped with VBus data interface. Outputs data every second at 9600 baud 8N1. To connect to this and read data from the bus a level shifting is needed as the voltage is around 8V. Although this is a symmetric connection supporting long wires for our purposes it's enough to measure its polarity with a voltmeter and adapt the level appropriately to 3.3V using the below circuit (many others exist but this is simple and works). Do not connect the GND pin of Wemos with the ground of Resol unit as that may damage the output port of it. The Wemos module needs its own power supply (double insulated recommended).

      The script (compile firmware with SML_REPLACE_VARS)
      >D
      +r="1,AA100021421000010774"
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,v,0,9600,Solar
      +%r%vo12ut@#,time,,zeit,1
      +%r%vo0sw@10,S1 COL,°C,sens1,1
      +%r%vo2sw@10,S2 TST1,°C,sens2,1
      +%r%vo4sw@10,S3 TST2,°C,sens3,1
      +%r%vo6sw@10,S4 TR,°C,sens4,1
      +%r%vo10ub@b0:1,R1 PUMP,,relay1,0
      +%r%vo10ub@b1:1,R2 VALVE,,relay2,0
      +%r%vo8ub@1,Pump1 speed,%%,pump1,0
      +%r%vo9ub@1,Pump2 speed,%%,pump2,0
      +%r%vo20uw@1,p1,Wh,p1,0
      +%r%vo22uw@1,p1000,Wh,p2,0
      +%r%vo24uw@1,p1000000,Wh,p3,0
      +%r%vo15ub@b0:1,Col Max,,col1,0
      +%r%vo15ub@b1:1,Col Min,,col2,0
      +%r%vo15ub@b2:1,Col Frost,,col3,0
      +%r%vo15ub@b3:1,Col Opt,,col4,0
      +%r%vo15ub@b4:1,Col Rueck,,col5,0
      +%r%vo15ub@b5:1,Col WMZ,,col6,0
      +#
      +

      Sanxing SX6x1 (SxxU1x) (Ascii OBIS)~

      Tested on SX631 (S34U18). Needs an RJ12 cable and a small adaptor circuit: (Note how power for the Wemos module is drawn directly from the meter. No external power supply needed)

      This meter sends bursts of data at 115200 baud every 10 seconds. Some data lines exceed 1038 characters. To adapt to these conditions, compile firmware with:

      #define SML_BSIZ 1060
      +#define MAX_METERS 1
      +#define TMSBSIZ 2048
      +#define USE_SML_SCRIPT_CMD
      +#define SML_REPLACE_VARS
      +

      View script
      >D
      +r="1,0-0:98.1.0(@("
      +;use a variable to store the decode string
      +>B
      +smlj=0
      +;don't send teleperiod MQTT at boot, because we can have 0 values (meter didn't send data yet)
      +->sensor53 r
      +>R
      +smlj=0
      +;don't send teleperiod MQTT at script restart, because we can have 0 values (meter didn't send data yet)
      +>S
      +if upsecs>22
      +then
      +smlj|=1
      +endif
      +;only send teleperiod MQTT if 22 seconds passed since boot (during this time meter most probably sent data)
      +>M 1
      ++1,3,o,16,115200,Name,1
      +1,1-0:32.7.0(@1,L1 Voltage,V,volts_l1,1
      +1,1-0:52.7.0(@1,L2 Voltage,V,volts_l2,1
      +1,1-0:72.7.0(@1,L3 Voltage,V,volts_l3,1
      +1,1-0:14.7.0(@1,Frequency,Hz,freq,2
      +1,0-0:96.14.0(@1,Current tariff,,tariff,0
      +1,=h<hr/>
      +1,1-0:1.8.0(@1,Energy import,kWh,enrg_imp,3
      +1,1-0:2.8.0(@1,Energy export,kWh,enrg_exp,3
      +1,1-0:1.8.1(@1,Energy import T1,kWh,enrg_imp_t1,3
      +1,1-0:1.8.2(@1,Energy import T2,kWh,enrg_imp_t2,3
      +1,1-0:2.8.1(@1,Energy export T1,kWh,enrg_exp_t1,3
      +1,1-0:2.8.2(@1,Energy export T2,kWh,enrg_exp_t2,3
      +1,1-0:1.7.0(@1,Power import,kW,pwr_imp,3
      +1,1-0:2.7.0(@1,Power export,kW,pwr_exp,3
      +1,1-0:13.7.0(@1,Power factor,,factor,3
      +1,=h<hr/>
      +1,1-0:3.8.0(@1,Reactive nrg import,kvarh,nrg_reac_imp,3
      +1,1-0:4.8.0(@1,Reactive nrg export,kvarh,nrg_reac_exp,3
      +1,1-0:5.8.0(@1,Reactive energy QI,kvarh,nrg_reac_q1,3
      +1,1-0:6.8.0(@1,Reactive energy QII,kvarh,nrg_reac_q2,3
      +1,1-0:7.8.0(@1,Reactive energy QIII,kvarh,nrg_reac_q3,3
      +1,1-0:8.8.0(@1,Reactive energy QIV,kvarh,nrg_reac_q4,3
      +1,1-0:5.7.0(@1,Reactive power QI,kvar,pwr_reac_q1,3
      +1,1-0:6.7.0(@1,Reactive power QII,kvar,pwr_reac_q2,3
      +1,1-0:7.7.0(@1,Reactive power QIII,kvar,pwr_reac_q3,3
      +1,1-0:8.7.0(@1,Reactive power QIV,kvar,pwr_reac_q4,3
      +1,=h<hr/>
      +1,=hPrevious month stats:
      +%r%1:1,Energy import,kWh,mo_enrg_imp,3
      +%r%2:1,Energy import T1,kWh,mo_enrg_impt1,3
      +%r%3:1,Energy import T2,kWh,mo_enrg_impt2,3
      +%r%4:1,Energy export,kWh,mo_enrg_exp,3
      +%r%5:1,Energy export T1,kWh,mo_enrg_expt1,3
      +%r%6:1,Energy export T2,kWh,mo_enrg_expt2,3
      +%r%7:1,Reactive nrg import,kvarh,mo_nrg_reac_imp,3
      +%r%8:1,Reactive nrg export,kvarh,mo_nrg_reac_exp,3
      +%r%9:1,Reactive energy QI,kvarh,mo_nrg_reac_q1,3
      +%r%10:1,Reactive energy QII,kvarh,mo_nrg_reac_q2,3
      +%r%11:1,Reactive energy QIII,kvarh,mo_nrg_reac_q3,3
      +%r%12:1,Reactive energy QIV,kvarh,mo_nrg_reac_q4,3
      +%r%13:1,Reactive energy SUM?,kvarh,mo_nrg_reac_sum,3
      +%r%14:1,Peak power import L1,kW,mo_pw_pk_in_l1,3
      +%r%15:1,Peak power import L2,kW,mo_pw_pk_in_l2,3
      +%r%16:1,Peak power import L3,kW,mo_pw_pk_in_l3,3
      +%r%17:1,Peak power export L1,kW,mo_pw_pk_ex_l1,3
      +%r%18:1,Peak power export L2,kW,mo_pw_pk_ex_l2,3
      +%r%19:1,Peak power export L3,kW,mo_pw_pk_ex_l3,3
      +#
      +
      Sample data
      /1234567890123
      +
      +0-0:1.0.0(202056789012W)
      +0-0:42.0.0(AUX1234567890123)
      +0-0:96.1.0(1234567890)
      +0-0:96.14.0(0001)
      +0-0:96.50.68(ON)
      +0-0:17.0.0(90.000*kW)
      +1-0:1.8.0(000258.072*kWh)
      +1-0:1.8.1(000103.782*kWh)
      +1-0:1.8.2(000154.290*kWh)
      +1-0:1.8.3(000000.000*kWh)
      +1-0:1.8.4(000000.000*kWh)
      +1-0:2.8.0(000048.367*kWh)
      +1-0:2.8.1(000032.813*kWh)
      +1-0:2.8.2(000015.554*kWh)
      +1-0:2.8.3(000000.000*kWh)
      +1-0:2.8.4(000000.000*kWh)
      +1-0:3.8.0(000003.513*kvarh)
      +1-0:4.8.0(000156.910*kvarh)
      +1-0:5.8.0(000003.498*kvarh)
      +1-0:6.8.0(000000.015*kvarh)
      +1-0:7.8.0(000027.718*kvarh)
      +1-0:8.8.0(000129.192*kvarh)
      +1-0:15.8.0(000306.440*kWh)
      +1-0:32.7.0(233.0*V)
      +1-0:52.7.0(230.6*V)
      +1-0:72.7.0(228.7*V)
      +1-0:31.7.0(002*A)
      +1-0:51.7.0(002*A)
      +1-0:71.7.0(001*A)
      +1-0:13.7.0(0.758)
      +1-0:33.7.0(0.615)
      +1-0:53.7.0(0.746)
      +1-0:73.7.0(0.937)
      +1-0:14.7.0(49.98*Hz)
      +1-0:1.7.0(00.000*kW)
      +1-0:2.7.0(00.854*kW)
      +1-0:5.7.0(00.000*kvar)
      +1-0:6.7.0(00.000*kvar)
      +1-0:7.7.0(00.735*kvar)
      +1-0:8.7.0(00.000*kvar)
      +0-0:98.1.0(210301000000W)(000249.070*kWh)(000100.816*kWh)(000148.254*kWh)(000047.903*kWh)(000032.349*kWh)(000015.554*kWh)(000003.513*kvarh)(000150.665*kvarh)(000003.498*kvarh)(000000.015*kvarh)(000027.119*kvarh)(000123.546*kvarh)(000296.974*kWh)(04.872*kW)(04.872*kW)(04.072*kW)(01.844*kW)(01.672*kW)(01.844*kW)
      +0-0:96.13.0(����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������)
      +!DA6A
      +

      SBC ALE3 (MODBus)~

      View script
      >DH
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,M,1,9600,SBC,1,1,02030023,02030028,0203002d,02030025,0203002a,0203002f,02030032,02030027,0203002c,02030031,02030021,02030015,02030018
      +1,020304UUuu@i0:1,Spannung L1,V,Voltage_L1,0
      +1,020304UUuu@i1:1,Spannung L2,V,Voltage_L2,0
      +1,020304UUuu@i2:1,Spannung L3,V,Voltage_L3,0
      +1,020304xxxxUUuu@i0:10,Strom L1,A,Current_L1,2
      +1,020304xxxxUUuu@i1:10,Strom L2,A,Current_L2,2
      +1,020304xxxxUUuu@i2:10,Strom L3,A,Current_L3,2
      +1,=h=
      +1,020304UUuu@i3:100,Leistung L1,kW,Power_L1,3
      +1,020304UUuu@i4:100,Leistung L2,kW,Power_L2,3
      +1,020304UUuu@i5:100,Leistung L3,kW,Power_L3,3
      +1,020304UUuu@i6:100,Leistung Total,kW,Power_Total,3
      +1,020304xxxxSSss@i3:100,BlindLeistung L1,kVAr,ReaktivePower_L1,3
      +1,020304xxxxSSss@i4:100,BlindLeistung L2,kVAr,ReaktivePower_L2,3
      +1,020304xxxxSSss@i5:100,BlindLeistung L3,kVAr,ReaktivePower_L3,3
      +1,020304xxxxSSss@i6:100,BLeistung Total,kVAr,ReaktivePower_Total,3
      +1,=h=
      +1,020304UUuu@i7:100,CosPhi L1,,CosPhi_L1,2
      +1,020304UUuu@i8:100,CosPhi L2,,CosPhi_L2,2
      +1,020304UUuu@i9:100,CosPhi L3,,CosPhi_L3,2
      +1,=h=
      +1,020304UUuuUUuu@i10:100,T2 Wert,kWh,T2_Value,2
      +#
      +

      SBC ALE3 2x (MODBus)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,M,1,9600,Meter,1,1,01030023,01030028,0103002d,01030025,0103002a,0103002f,01030032,01030027,0103002c,01030031,0103001B,0103001d,03030023,03030028,0303002d,03030025,0303002a,0303002f,03030032,03030027,0303002c,03030031,0303001B,0303001d
      +1,=h Domestic Electricity:
      +1,010304UUuuUUuu@i10:100,1 Tariff 1 total,kWh,M1_T1_total,2
      +1,010304UUuuUUuu@i11:100,1 Tariff 1 partial,kWh,M1_T1_par,2
      +1,=h Readings:
      +1,010304UUuu@i0:1,1 Voltage L1,V,M1_Voltage_L1,0
      +1,010304UUuu@i1:1,1 Voltage L2,V,M1_Voltage_L2,0
      +1,010304UUuu@i2:1,1 Voltage L3,V,M1_Voltage_L3,0
      +1,010304xxxxUUuu@i0:10,1 Current L1,A,M1_Current_L1,2
      +1,010304xxxxUUuu@i1:10,1 Current L2,A,M1_Current_L2,2
      +1,010304xxxxUUuu@i2:10,1 Current L3,A,M1_Current_L3,2
      +1,010304UUuu@i3:100,1 Active Power L1,kW,M1_PRMS_L1,3
      +1,010304UUuu@i4:100,1 Active Power L2,kW,M1_PRMS_L2,3
      +1,010304UUuu@i5:100,1 Active Power L3,kW,M1_PRMS_L3,3
      +1,010304UUuu@i6:100,1 Active Power total,kW,M1_PRMS_total,3
      +1,010304xxxxSSss@i3:100,1 Reactive Power L1,kVAr,M1_QRMS_L1,3
      +1,010304xxxxSSss@i4:100,1 Reactive Power L2,kVAr,M1_QRMS_L2,3
      +1,010304xxxxSSss@i5:100,1 Reactive Power L3,kVAr,M1_QRMS_L3,3
      +1,010304xxxxSSss@i6:100,1 Reactive Power total,kVAr,M1_QRMS_total,3
      +1,010304UUuu@i7:100,1 CosPhi L1,,M1_CosPhi_L1,2
      +1,010304UUuu@i8:100,1 CosPhi L2,,M1_CosPhi_L2,2
      +1,010304UUuu@i9:100,1 CosPhi L3,,M1_CosPhi_L3,2
      +1,=h________________________________________________
      +; meter 2 +12 offset
      +1,=h Heat Pump
      +1,030304UUuuUUuu@i22:100,2 Tariff 1 total,kWh,M2_T1_total,2
      +1,030304UUuuUUuu@i23:100,2 Tariff 1 partial,kWh,M2_T1_par,2
      +1,=h Readings:
      +1,030304UUuu@i12:1,2 Voltage L1,V,M2_Voltage_L1,0
      +1,030304UUuu@i13:1,2 Voltage L2,V,M2_Voltage_L2,0
      +1,030304UUuu@i14:1,2 Voltage L3,V,M2_Voltage_L3,0
      +1,030304xxxxUUuu@i12:10,2 Current L1,A,M2_Current_L1,2
      +1,030304xxxxUUuu@i13:10,2 Current L2,A,M2_Current_L2,2
      +1,030304xxxxUUuu@i14:10,2 Current L3,A,M2_Current_L3,2
      +1,030304UUuu@i15:100,2 Active Power L1,kW,M2_PRMS_L1,3
      +1,030304UUuu@i16:100,2 Active Power L2,kW,M2_PRMS_L2,3
      +1,030304UUuu@i17:100,2 Active Power L3,kW,M2_PRMS_L3,3
      +1,030304UUuu@i18:100,2 Active Power total,kW,M2_PRMS_total,3
      +1,030304xxxxSSss@i15:100,2 Reactive Power L1,kVAr,M2_QRMS_L1,3
      +1,030304xxxxSSss@i16:100,2 Reactive Power L2,kVAr,M2_QRMS_L2,3
      +1,030304xxxxSSss@i16:100,2 Reactive Power L3,kVAr,M2_QRMS_L3,3
      +1,030304xxxxSSss@i18:100,2 Reactive Power total,kVAr,M2_QRMS_total,3
      +1,030304UUuu@i19:100,2 CosPhi L1,,M2_CosPhi_L1,2
      +1,030304UUuu@i20:100,2 CosPhi L2,,M2_CosPhi_L2,2
      +1,030304UUuu@i21:100,2 CosPhi L3,,M2_CosPhi_L3,2
      +#
      +

      Schneider iEM3155 (MODBus)~

      Set device parity to NONE

      View script
          >D  
      +>B  
      +->sensor53 r
      +>M 1  
      ++1,3,m,0,19200,iEM3155,1,1,0103B02D,0103B02B,01030BF3,01030BED,01030BEF,01030BF1,01030BD3,01030BD5,01030BD7,01030BB7,01030BB9,01030BBB,01030C0B,01030C25
      +; ***************************************
      +; *   Schneider iEM3155 Energy Meter    *
      +; ***************************************
      +; Serial: 19200
      +; Set device parity to NONE
      +; Slave address: 0x01
      +; https://download.schneider-electric.com/files?p_Doc_Ref=DOCA0005EN&p_enDocType=User+guide&p_File_Name=DOCA0005EN-13.pdf
      +1,010304ffffffff@i0:1,Gesamteinspeisung,kWh,Gesamteinspeisung,0
      +1,010304ffffffff@i1:1,Gesamtverbrauch,kWh,Gesamtverbrauch,0
      +1,010304ffffffff@i2:0.001,Momentanverbrauch,W,Momentanverbrauch,0
      +1,010304ffffffff@i3:1,L1 Wirkenergie,kW,L1Wirkenergie,3
      +1,010304ffffffff@i4:1,L2 Wirkenergie,kW,L2Wirkenergie,3
      +1,010304ffffffff@i5:1,L3 Wirkenergie,kW,L3Wirkenergie,3
      +1,010304ffffffff@i6:1,L1 Spannung,V,L1Spannung,0
      +1,010304ffffffff@i7:1,L2 Spannung,V,L2Spannung,0
      +1,010304ffffffff@i8:1,L3 Spannung,V,L3Spannung,0
      +1,010304ffffffff@i9:1,L1 Strom,A,L1Strom,2
      +1,010304ffffffff@i10:1,L2 Strom,A,L2Strom,2
      +1,010304ffffffff@i11:1,L3 Strom,A,L3Strom,2
      +1,010304ffffffff@i12:1,Leistungsfaktor,,Leistungsfaktor,2
      +1,010304ffffffff@i13:1,Frequenz,Hz,Frequenz,0
      +#
      +

      SDM230 (MODBus)~

      View script
      >D  
      +ms="1,010404ffffffff@"  
      +>B  
      +->sensor53 r  
      +>M 1  
      ++1,3,m,0,9600,PV,1,2,01040000,01040006,0104000C,01040012,01040018,0104001E,01040024,01040046,01040048,0104004A,0104004C,0104004E,01040054,01040056,01040058,0104005A,0104005C,0104005E,01040102,01040108,01040152,01040158,01040180,01040182  
      +%ms%i0:1,Volt,V,Volt,2  
      +%ms%i1:1,Strom P1,A,Strom,3  
      +%ms%i2:1,*,W,Leistung,2  
      +%ms%i3:1,Scheinleistung,VA,ScheinLeistung,2  
      +%ms%i4:1,Blindleistung,VAr,Blindleistung,2  
      +%ms%i5:1,P-Faktor,,P_Faktor,1  
      +%ms%i6:1,cosPhi,°,cosPhi,2  
      +%ms%i7:1,Frequenz,Hz, Frequenz,1  
      +%ms%i8:1,Wirkleistung Import,kWh,Wirkleistung_Im1,3  
      +%ms%i9:1,Wirkleistung Export,kWh,Wirkleistung_Ex,3  
      +%ms%i10:1,Blindleistung Import,VkkVARh,Blindleistung_Im,3  
      +%ms%i11:1,Blindleistung Export,VkkVARh,Blindleistung_Ex,3  
      +%ms%i12:1,Gesamtleistungsbedarf,W,GesLeistBed,2  
      +%ms%i13:1,GesamtLeistung Max,W,GesLeistMax,2  
      +%ms%i14:1,Akt.Nachfrage,W,AktNachfrage,2  
      +%ms%i15:1,Rückleistungs Bed,W,RueckLeistBed,2  
      +%ms%i16:1,Rückleistungs Bed Max,W,RueckLeistBedMax,2  
      +%ms%i17:1,Strom Nachfrage,A,StromNachfrage2,2  
      +%ms%i19:1,Max Strombedarf,A,StromBedMax,2  
      +%ms%i20:1,Wirkleistung Gesamt,kWh,Wirkleistung_total,2  
      +%ms%i21:1,Blindleistung Gesamt,kVARh,Blindleistung_total,2  
      +%ms%i22:1,Temp Gesamtleistung,kWh,TempGesamtLeist,2  
      +#  
      +

      SDM530 (MODBus)~

      View script
      >D  
      +>B  
      +->sensor53 r
      +>M 1  
      ++1,3,m,0,9600,MODBUS,1,1,01040000,01040002,01040004,01040006,01040008,0104000a,0104000c,0104000e,01040010  
      +1,010404ffffffff@i0:1,Voltage P1,V,Voltage_P1,2  
      +1,010404ffffffff@i1:1,Voltage P2,V,Voltage_P2,2  
      +1,010404ffffffff@i2:1,Voltage P3,V,Voltage_P3,2  
      +1,010404ffffffff@i3:1,Current P1,A,Current_P1,2  
      +1,010404ffffffff@i4:1,Current P2,A,Current_P2,2  
      +1,010404ffffffff@i5:1,Current P3,A,Current_P3,2  
      +1,010404ffffffff@i6:1,Active Power P1,W,Power_P1,2  
      +1,010404ffffffff@i7:1,Active Power P2,W,Power_P2,2  
      +1,010404ffffffff@i8:1,Active Power P3,W,Power_P3,2  
      +#  
      +

      SDM72D (MODBus)~

      Script to extract readings from Eastron SDM72D Series devices (tested on SDM72D-M). Manual with comprehensive documentation about all Modbus registers available here.

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,25,mN1,0,9600,SDM72D,26,1,01040000,01040002,01040004,01040006,01040008,0104000a,0104000c,0104000e,01040010,01040012,01040014,01040016,01040018,0104001a,0104001c,0104001e,01040020,01040022,0104002a,0104002e,01040030,01040034,01040038,0104003c,0104003e,01040046,01040048,0104004A,01040156,01040158,0104018c,01040500,01040502
      +1,010404ffffffff@i0:1,Voltage P1,V,voltage_phase1,2
      +1,010404ffffffff@i1:1,Voltage P2,V,voltage_phase2,2
      +1,010404ffffffff@i2:1,Voltage P3,V,voltage_phase3,2
      +1,010404ffffffff@i3:1,Current P1,A,current_phase1,2
      +1,010404ffffffff@i4:1,Current P2,A,current_phase2,2
      +1,010404ffffffff@i5:1,Current P3,A,current_phase3,2
      +1,010404ffffffff@i6:1,Power P1,W,power_phase1,2
      +1,010404ffffffff@i7:1,Power P2,W,power_phase2,2
      +1,010404ffffffff@i8:1,Power P3,W,power_phase3,2
      +1,010404ffffffff@i9:1,Power Apparent P1,VA,power_apparent_phase1,2
      +1,010404ffffffff@i10:1,Power Apparent P2,VA,power_apparent_phase2,2
      +1,010404ffffffff@i11:1,Power Apparent P3,VA,power_apparent_phase3,2
      +1,010404ffffffff@i12:1,Power Reactive P1,VAr,power_reactive_phase1,2
      +1,010404ffffffff@i13:1,Power Reactive P2,VAr,power_reactive_phase2,2
      +1,010404ffffffff@i14:1,Power Reactive P3,VAr,power_reactive_phase3,2
      +1,010404ffffffff@i15:1,Power Factor P1,,power_factor_phase1,2
      +1,010404ffffffff@i16:1,Power Factor P2,,power_factor_phase2,2
      +1,010404ffffffff@i17:1,Power Factor P3,,power_factor_phase3,2
      +1,010404ffffffff@i18:1,Average Voltage,V,voltage_avg,2
      +1,010404ffffffff@i19:1,Average Current,A,current_avg,2
      +1,010404ffffffff@i20:1,Current Total,A,current_total,2
      +1,010404ffffffff@i21:1,Power Total,W,power_total,2
      +1,010404ffffffff@i22:1,Power Apparent Total,VA,power_apparent_total,2
      +1,010404ffffffff@i23:1,Power Reactive Total,VAr,power_reactive_total,2
      +1,010404ffffffff@i24:1,Power Factor Total,,power_factor_total,2
      +1,010404ffffffff@i25:1,Frequency,Hz,frequency,2
      +1,010404ffffffff@i26:1,Energy Imported,kWh,energy_imported,3
      +1,010404ffffffff@i27:1,Energy Exported,kWh,energy_exported,3
      +1,010404ffffffff@i28:1,Energy Total,kWh,energy_total,3
      +1,010404ffffffff@i29:1,Energy Reactive Total,kVArh,energy_reactive_total,3
      +1,010404ffffffff@i30:1,Net Energy,kWh,energy_net,3
      +1,010404ffffffff@i31:1,Import Power,W,power_import,2
      +1,010404ffffffff@i32:1,Export Power,W,power_export,2
      +#
      +

      Siemens TD-3511~

      This device is used in the grid of EGTF - Elektrizitäts-Genossenschaft Tacherting-Feichten eG. Read uses IEC 62056-21 data mode "C" without acknowledgement by the reading device.

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,o,0,300,STROM,1,600,2F3F210D0A
      +1,1.8.1(@1,Total Consumed,KWh,Total_in,3
      +1,2.8.1(@1,Total Delivered,KWh,Total_out,3
      +1,0.0.0(@#),Meter Number,,Meter_number,0
      +#
      +

      Trovis 557x (MODBus)~

      These heating regulators have a lot of registers. If your station number is different from standard (247 ==> 0xF7) you have got to change every first byte accordingly.

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,m,0,19200,Trovis,1,2,rF7030009000E,rF703001C0004,F703006A
      +1,F7031CSSss@i0:10,Außentemp.,°C,Temp_Outside,1
      +1,F7031CxxxxxxxxxxxxSSss@i0:10,Vorlauftemp.,°C,Temp_Flow,1
      +1,F7031CxxxxxxxxxxxxxxxxxxxxxxxxxxxxSSss@i0:10,Rücklauftemp.,°C,Temp_Return,1
      +1,F7031CxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxSSss@i0:10,Speichertemp.,°C,Temp_Vessel,1
      +1,F70308UUuu@i1:1,MesswertImp-h,imp/h,Metric_ImpH,0
      +1,F70308xxxxUUuu@i1:100,Messwertm3-h,m³/h,Metric_M3H,2
      +1,F70308xxxxxxxxUUuu@i1:10,AA10-10V,V,Metric_AA10,1
      +1,F70308xxxxxxxxxxxxUUuu@i1:10,AA20-10V,V,Metric_AA20,1
      +1,F70304UUuu@i2:1,StellsignalRk1,%,CtrlSig_RK1,0
      +#
      +

      WOLF CSZ 11/300 Heater (EBus)~

      View script
      >D  
      +>B  
      +->sensor53 r
      +>M 1  
      ++1,3,e,0,2400,EBUS  
      +1,xxxx0503xxxxxxxxxxxxxxxxss@1,Outside temperature,C,Outsidetemp,0  
      +1,xxxx5014xxxxxxxxxxuu@1,Romm temperature,C,Roomtemp,0  
      +1,xxxx0503xxxxxxxxxxxxxxuu@1,Warmwater,C,Warmwater,0  
      +1,xxxx0503xxxxxxxxxxuu@1,Boiler,C,Boiler,0  
      +1,03fe0503xxxxxxxxxxxxuu@1,Returns,C,Returns,0  
      +1,03fe0503xxxxuu@1,Status,,Status,0  
      +1,03fe0503xxxxxxuu@b3:1,Burner on,,Burner,0  
      +1,xxxx5017xxxxxxuuuu@16,Solar collektor,C,Collector,1  
      +1,xxxx5017xxxxxxxxxxuuuu@16,Solar storage,C,Solarstorage,1  
      +1,xxxx5017xxuu@b0:1,Solar pump on,,Solarpump,0  
      +#  
      +

      ZPA GH302 (SML)~

      View script
      >D
      +>B
      +=>sensor53 r
      +>M 1
      ++1,3,s,0,9600,Strom
      +1,77070100010800ff@1000,Bezug,kWh,Total_in,0
      +1,77070100020800ff@1000,Einspeisung,kWh,Total_out,0
      +#
      +

      ZPA GH305 (SML)~

      View script
      >D
      +>B
      +->sensor53 r
      +>M 1
      ++1,3,s,0,9600,SML
      +1,77070100010800ff@1000,Total Verbrauch,KWh,Total_in,3
      +1,77070100020800ff@1000,Total Einspeisung,kWh,Total_out,3
      +1,=h==================
      +1,77070100100700ff@1,Actual load,W,Power_curr,0
      +1,=h==================
      +1,=m 9+10+11 @1,Currents L1+L2+L3,A,Curr_summ,3
      +1,=m 12+13+14/#3 @1,Voltage L1+L2+L3/3,V,Volt_avg,3
      +1,=h==================
      +1,77070100240700ff@1,Consumption P1,W,Power_p1,2
      +1,77070100380700ff@1,Consumption P2,W,Power_p2,2
      +1,770701004c0700ff@1,Consumption P3,W,Power_p3,2
      +1,=h   ----
      +1,770701001f0700ff@1,Current L1,A,Curr_p1,3
      +1,77070100330700ff@1,Current L2,A,Curr_p2,3
      +1,77070100470700ff@1,Current L3,A,Curr_p3,3
      +1,=h   ----
      +1,77070100200700ff@1,Voltage L1,V,Volt_p1,3
      +1,77070100340700ff@1,Voltage L2,V,Volt_p2,3
      +1,77070100480700ff@1,Voltage L3,V,Volt_p3,3
      +1,=h==================
      +1,77070100510701ff@1,Phaseangle L2-L1,deg,phase_angle_L2_L1,0
      +1,77070100510702ff@1,Phaseangle L3-L1,deg,phase_angle_L3_L1,0
      +1,77070100510704ff@1,Phaseangle I/U L1,deg,phase_angle_L1,1 
      +1,7707010051070fff@1,Phaseangle I/U L2,deg,phase_angle_L2,1  
      +1,7707010051071aff@1,Phaseangle I/U L3,deg,phase_angle_L3,1 
      +1,770701000e0700ff@1,Frequency,Hz,Freq,1
      +;1,=h=======UNBEKANNT===========
      +;1,77070100000009FF@#,unbekannt1,,Power_Use_Sum,3
      +;1,77070100000000FF@#,unbekannt2,,Power_Use_Sum,3
      +;1,7707010060320101@#,unbekannt3,,Meter_id,0
      +;1,77010b0a01445a47@#,unbekannt4,,Meter_id,0
      +;1,77070100600100ff@#,unbekannt5,,Meter_id,0
      +1,=h
      +#
      +

      inepro Metering PRO380-MB (M-Bus)~

      This is a controller for standard solar thermal systems equipped with VBus data interface. Outputs data every second at 9600 baud 8N1. To connect to this and read data from the bus a level shifting is needed as the voltage is around 8V. Although this is a symmetric connection supporting long wires for our purposes it's enough to measure its polarity with a voltmeter and adapt the level appropriately to 3.3V using the below circuit (many others exist but this is simple and works). Do not connect the GND pin of Wemos with the ground of Resol unit as that may damage the output port of it. The Wemos module needs its own power supply (double insulated recommended).

      The script (compile firmware with USE_SML_M)

      >D
      +>B
      +->sensor53 r 
      +; ->sensor53 d1 ; Dump mode for console debug
      +>M 1
      ++1,16,rE1,0,2400,,17,100,680303685300b40716,105b005b16,680303685300B10416
      +1,4BFD47bcd6@100,L1 voltage,V,L1_V,2
      +1,8B01FD47bcd6@100,L2 voltage,V,L2_V,2
      +1,CB01FD47bcd6@100,L3 voltage,V,L3_V,2
      +1,4BFD59bcd6@100,L1 current,A,L1_I,2
      +1,8B01FD59bcd6@100,L2 current,A,L2_I,2
      +1,CB01FD59bcd6@100,L3 current,A,L3_I,2
      +1,4C2Abcd5@1000,L1 active power,kW,L1_P,3
      +1,8C012Abcd5@1000,L2 active power,kW,L2_P,3
      +1,CC012Abcd5@1000,L3 active power,kW,L3_P,3
      +1,0C2Abcd6@1000,Total active power,kW,T_P,3
      +1,4C04bcd8@100,L1 total energy,kWh,L1_TE,2
      +1,8C0104bcd8@100,L2 total energy,kWh,L2_TE,2
      +1,CC0104bcd8@100,L3 total energy,kWh,L3_TE,2
      +1,000C04bcd8@100,Total active energy,kWh,TE,2
      +1,6C04bcd8@100,L1 reverse energy,kWh,L1_TRE,2
      +1,AC0104bcd8@100,L2 reverse energy,kWh,L2_TRE,2
      +1,EC0104bcd8@100,L3 reverse energy,kWh,L3_TRE,2
      +1,2C04bcd8@100,Total reverse energy,kWh,TRE,2
      +#
      +

      AEConversion solar inverter INVXXX (RAW)~

      Tested on an AEConversion INV500-90 with RS485 interface.

      View script
      >D
      +>B
      +=>sensor53 r
      +; Monitor Sensor at GPIO25
      +=>sensor53 l255
      +>M 1
      ++1,13,r,0,9600,aec,15,50,2101B203FD4D0D
      +
      +1,212717UUuux7@1,Leistung,W,power,0
      +1,212717x4UUuux4@1000,Energie,kWh,energy_sun,3
      +#
      +

      SMA Solar Inverter (TCP MODBus)~

      View script
      >D
      +>B
      +=>sensor53 r
      +>M1
      ++1,[192.168.56.91],m,0,502,SMA,0,10,03047741,03047747,03047777,03047831,03047833,03047835,030478ED,030478EF,030478F1,03047893,030478E9,0304787D,03047881,r03047AA50004,r03047AA90004
      +1,030404U32@i0:1000,Gesamtertrag,kWh,v1,3
      +1,030404U32@i1:1000,Tagesertrag,kWh,v2,3
      +1,030404U32@i2:1000,Einspeisung_ges,kWh,v3,3
      +1,030404S32@i3:100,DC Str. A,A,v4,2
      +1,030404S32@i4:100,DC Sp. A,V,v5,2
      +1,030404S32@i5:100,DC Le. A,W,v6,2
      +1,030404S32@i6:100,DC Str. B,A,v7,2
      +1,030404S32@i7:100,DC Sp. B,V,v8,2
      +1,030404S32@i8:100,DC Le. B,W,v9,2
      +;
      +1,030404S32@i9:100,AC Le.,W,v10,2
      +;
      +1,030404S32@i10:10,WR_Temp,°C,v11,2
      +;
      +1,030404U32@i11:1,Batterieladung,%%,v12,0
      +1,030404S32@i12:10,Batt_Temp,°C,v13,2
      +;
      +1,030408U64@i13:1000,Batt_Ladung,kWh,v14,3
      +1,030408U64@i14:1000,Batt_EntLadung,kWh,v15,3
      +#
      +
      \ No newline at end of file diff --git a/SolaX-X1/index.html b/SolaX-X1/index.html new file mode 100644 index 0000000000..d41678fda3 --- /dev/null +++ b/SolaX-X1/index.html @@ -0,0 +1,13 @@ + SolaX X1 - Tasmota
      Skip to content

      SolaX Power - Single phase string inverter X1~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_SOLAX_X1
      +#define USE_SOLAX_X1
      +#endif
      +

      There are additional #define compiler directive parameters which can be used, if they are necessary for your setup.
      Set an other serial speed than the default value of 9600:

      #undef  SOLAXX1_SPEED
      +#define SOLAXX1_SPEED  9600
      +
      If your inverter has two PV-inputs, you can activate the 2nd one:
      #ifndef SOLAXX1_PV2
      +#define SOLAXX1_PV2
      +#endif
      +
      By using the standard firmware variant with Solax-X1 on top, the resulting binary fits into a normal 1M chip. If you need more space, e.g for other modules, you can disable the ReadConfig command to save about 3k1 bytes of code. To do this, simply remove the compiler directive:
      #define SOLAXX1_READCONFIG
      +
      This module works as energy sensor. So be sure to define this, if not already done:
      #ifndef USE_ENERGY_SENSOR
      +#define USE_ENERGY_SENSOR
      +#endif
      +

      General~

      This module reads runtime values from a Solax X1 device via RS485 Modbus interface and publishes them to MQTT.
      X1 Mini X1 Air X1 Smart
      The communication of this module is based on the description of the communication protocol version 1.8.

      Wiring~

      To connect the inverter to the Tasmota-device, you have to use a breakout board to adapt the RS485 interface of the inverter to serial interface of the ESP.

      Breakout boards~

      There are many RS485-to-TTL modules, aka breakout boards, available. They may work or not. You should have attention on the operation voltage. The ESP-devices work with 3 volts. Because of that be careful experimenting with 5 volts. In the best case nothing works. In the worst case it will destroy your ESP or breakout board. Here are two examples of tested breakout boards. Recommended is a board with a SP3485 chip, because it is designed for operating at 3 volts.

      SP3485~

      The SP3485 breakout board is specially made to work with only 3 volts. It has a separate RTS-pin and works with a voltage from 3 to 5 volts.
      SP3485_Breakout1 SP3485_Breakout2

      HW-0519~

      The HW-0519 breakout board does not need a separate RTS-pin, because it automatically switches between sending and receiving. The recommended voltage is 5 volts, but it should also work with 3 volts.
      HW-0519_Breakout

      ESP ⬌ breakout board~

      The RX-, TX- and RTS- (if needed) lines have to be connected to the ESP matching the module configuration.

      ESP SP3485 HW-0519
      3.3V 3-5V VCC
      GND GND GND
      RX TX-O RXD
      TX RX-I TXD
      RTS RTS -

      Breakout board ⬌ inverter~

      The RS485 interface is a 2-wire-connection. The wires are called A+ and B-. The big advantage of the interface is, beside of needing only two wires, that it can reach a length up to 1200 meters. The inverter has a RJ45-jack, where the interface is accessible. Please consult the manual of your inverter where it is located.

      Tip: You can use an ethernet cable and cut off one connector. The RS485 interface uses the blue wire pair.

      Breakout board RJ45 inverter Wire color (T568B)
      A (+) Pin 4 blue
      B (-) Pin 5 blue-white
      G / Ground Pin 7 brown-white

      Info

      In many cases two wires are enough to keep it working without errors. When your environment has electrical interferences or your cable is quiet long, you should use a third wire to establish a common signal reference. This wire has to be connected to the Ground pins.

      Configuration~

      You have to configure the module or the template. Select SolaxX1 Tx and SolaxX1 Rx for the RS485 communication. If you have a breakout board which needs the RTS line, you must also select SolaxX1 RTS.
      x1-config

      Operation~

      Result~

      When every thing works you will see the current data on the main page. They are also provided via MQTT.
      x1-example

      Tips

      • To send a MQTT telemetry message immediately on every change of power, you can set a PowerDelta value.
        E.g. PowerDelta 101 for every change of at least 1 W.
      • Set SetOption72 to 1 for displaying the value of total energy reported from the inverter.

      Console commands~

      There are two special console commands for the X1 converter:
      EnergyConfig ReadIDinfo reads and displays ID-data from the inverter
      EnergyConfig ReadConfig reads and displays the configuration from the inverter

      Inverter status~

      The inverter status field represents the value reported by the inverter, when the inverter is sending data. In the case when no data is received, it will be display off. As the converter is only working, when the sun is shining, you will see off normally at night or too low light.

      Tip

      When the inverter is working and off is displayed, so you have to check your hard- and software setup.

      \ No newline at end of file diff --git a/Sonoff-DIY/index.html b/Sonoff-DIY/index.html new file mode 100644 index 0000000000..56a5900591 --- /dev/null +++ b/Sonoff-DIY/index.html @@ -0,0 +1,149 @@ + Sonoff DIY - Tasmota
      Skip to content

      Sonoff DIY

      Some new Sonoff devices support the new Itead DIY architecture which allows OTA firmware upload. With Sonoff DIY, a user has more control over the hardware features of the device and also allows for upgrading the firmware without additional hardware. The following procedure upgrades Sonoff eWelink firmware to Tasmota.

      There are many reports this procedure has changed with newer versions of Sonoff DIY

      Tasmota does not provide any kind of support for flashing using this method. Please contact Sonoff Support for help.

      IMPORTANT: There are some reports suggesting that the Windows version of Sonoff DIY Tool contains a trojan. It is not clear if it actually contains the malicious code or these are just false positives due to the way Python code was converted to native executables. Nevertheless, proceed with care.

      Compatible devices~

      Currently the following devices officially support Sonoff DIY: - Sonoff Basic R3 - Sonoff RF R3 - Sonoff Mini

      As Sonoff DIY is enabled by connecting GPIO16 to GND it may well be possible that other Sonoff devices running eWelink will support it.

      Note

      The OTA process Sonoff provides through the Sonoff DIY procedure does not create a backup of the Itead firmware on the device. If you use this OTA method to flash Tasmota on the Sonoff device, you will not be able to revert to the original factory firmware. ⚠

      Flash procedure~

      Guide originally from @Brunas

      1. Pair the device with the eWeLink app and update firmware. The wifi network you connect to during this step will need to be reachable in order to enter DIY mode.
      2. Follow instructions how to enter DIY mode from Sonoff. This is the excerpt from it:

        1. Long press the button for 5 seconds to enter pairing mode, then press another 5 seconds to ender Compatible Pairing Mode (AP). The LED indicator should blink continuously.
        2. From mobile phone or PC WiFi setting, an Access Point of the device named ITEAD-XXXXXXXX will be found, connect it with default password 12345678
        3. Open the browser and access http://10.10.7.1/
        4. Next, Fill in WiFi SSID and password. Once successfully connected, the device is in DIY mode.

      Note: I needed to manually change IP address to 10.10.7.2, 255.0.0.0 with gateway 10.10.7.1 in adapter TCP/IPv4 settings to access that IP address.

      1. Use Fing or any similar local network scanning app on your smartphone or PC to find IP address of your Sonoff Mini device. MAC Vendor most likely is Espressif and the device has 8081 port open.
      2. Check that diy mode is working properly.

      $SONOFF_IP must be defined with the IP or FQDN of the intended Sonoff device before running the curl command. With curl:

      SONOFF_IP="10.0.0.2"
      +curl -XPOST --header "Content-Type: application/json" --data-raw '{"deviceid": "", "data": {}}' http://$SONOFF_IP:8081/zeroconf/info
      +

      Or with the Rester browser extension: Install **Rester** extension in Chrome or Firefox or any other preferred tool to perform REST API operations. To test your device DIY mode create new request in **Rester**: 1. Method: **POST** 2. URL: http://<*IP of your device*>:8081/zeroconf/info 3. Body: `{"data": {}}` 4. You might need to add Header **Content-Type** with value **application/json** 5. Press **SEND**

      If all is OK, status code 200 should be returned with bunch of data:

      {
      +    "seq": 1,
      +    "error": 0,
      +    "data": {
      +        "switch": "off",
      +        "startup": "off",
      +        "pulse": "off",
      +        "pulseWidth": 2000,
      +        "ssid": "YourWiFi",
      +        "otaUnlock": false,
      +        "fwVersion": "3.6.0",
      +        "deviceid": "YourDeviceId",
      +        "bssid": "YourBSSId",
      +        "signalStrength": -52
      +    }
      +}
      +
      If that doesn't return 200, try going back to 5s+5s reset above. 4. If all above works, let's unlock OTA:

      With curl:

      curl -XPOST --header "Content-Type: application/json" --data-raw '{"deviceid": "", "data": {}}' http://$SONOFF_IP:8081/zeroconf/ota_unlock
      +
      Or with the Rester browser extension: 1. Method: **POST** 2. URL: http://<*IP of your device*>:8081/zeroconf/ota_unlock 3. Body: `{"data": {}}` 4. You might need to add Header **Content-Type** with value **application/json** 5. Press **SEND** 6. You should get status code *200*

      Optionally for curiosity you could retry info query to check if otaUnlock value now is true 5. Download the appropriate binary from http://ota.tasmota.com/tasmota/release and flash it. NOTE: The maximum firmware size is 508kb, which precludes the standard release binary. Absolutely do not use tasmota-minimal at this stage, this would brick your device.

      There are a number of reported issues with the stock firmware's OTA behavior, so it may be easier to use an existing server that works around these issues.
      $HASH must be defined with the sha256sum of the intended firmware file (the .bin file) before running the curl command.
      For example:

      HASH="f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
      +curl -XPOST --data "{\"deviceid\":\"\",\"data\":{\"downloadUrl\": \"http://sonoff-ota.aelius.com/tasmota-latest-lite.bin\", \"sha256sum\": \"$HASH\"} }" http://$SONOFF_IP:8081/zeroconf/ota_flash
      +

      You're now ready to configure tasmota.

      Video tutorials~

      More info:~

      \ No newline at end of file diff --git a/Sonoff-SPM/index.html b/Sonoff-SPM/index.html new file mode 100644 index 0000000000..aeaab0069f --- /dev/null +++ b/Sonoff-SPM/index.html @@ -0,0 +1,6 @@ + Sonoff SPM - Tasmota
      Skip to content

      Sonoff SPM~

      This feature is included only in tasmota32 binaries

      Do not use this device as safety fuse!

      Info

      For ARM firmware upgrades it's advised to make a full backup of the SPM-Main ESP 4M flash firmware before flashing Tasmota.

      The Sonoff Smart Stackable Power Meter uses a SPM-Main unit with ESP32 providing Wi-Fi and ethernet connections. A serial interface and a SPI interface connects to an ARM MCU which in turn provides a RS-485 bus to up to 32 SPM-4Relay modules. The SPM-4Relay module contains an ARM MCU too providing independent power management for four bi-stable relays rated for 20A at 240V for a total of 4400W.

      Note

      As of this writing Tasmota v11.1.0.3 supports ARM firmware versions 1.0.0 and 1.2.0.

      The firmware monitors the attached SPM-4Relay modules and stores energy usage history for up to 180 days on an optional SD-Card accessible by the ARM MCU only. The ARM firmware provides numerous un-documented functions allowing the ESP32 to send and receive information.

      Background information~

      More information about the SPM can be obtained here:

      Tasmota~

      Tasmota, installed on the ESP32, can connect to the SPM-Main ARM MCU using the serial interface and provides the following functionality:

      • Support for up to 8 SPM-4Relays limited by current register usage
      • Power control of all 32 relays using standard features
      • Energy usage using standard features
      • Overload detection using ARM firmware
      • GUI display of rotating relays or a user selected 4Relay module
      • Fix firmware max 180 days energy usage by storing daily Energy Total in Tasmota's filesystem
      • Mapping physical relays to scanned relays

      Limitations~

      The following notes currently apply:

      • Tasmota is unable to upgrade the ARM firmware. In case an upgrade is wanted install the backed-up sonoff firmware and perform the upgrade using the eWeLink app. Once upgraded make another backup before restoring Tasmota for future ARM firmware upgrades.

      Configuration~

      In addition to installing the tasmota32.bin image some configuration might be needed.

      Relay mapping~

      After a restart the ARM firmware starts to scan for available 4Relay modules. Every module has a unique id which is send to Tasmota in random order. For a user to pinpoint a physical set of four relays it is therefor needed to "map" the scanned modules once. Tasmota stores the id's in a mapping table build using the command SSPMMap <scanned module number>,<scanned module number>,.. where the first entry will map to physical relays 1 to 4, the second entry will map to physical relays 5 to 8 etc. The scanned module information needed is displayed on the console during restart or on request by executing command SSPMScan with a weblog 2 setting.

      Note

      Scanning takes over 20 seconds so be patient

      Look for the below information during a restart for a two 4Relay module system:

      00:00:00.123 Project tasmota - Sspm2 Version 11.0.0.1(tasmota)-2_0_2_2(2022-02-18T12:09:05)
      +00:00:05.191 CFG: SPM loaded from file
      +00:00:08.305 SPM: Main version 1.0.0 found
      +13:54:05.392 SPM: 4Relay 1 (mapped to 2) type 130 version 1.0.0 found with id 8B343237393734134B353637
      +13:54:05.401 SPM: 4Relay 2 (mapped to 1) type 130 version 1.0.0 found with id 6B7E3237393734134B353637
      +

      In this case the two modules are mapped using command SSPMMap 2,1. As Tasmota does store the ids of the 4Relay modules a future hussle of the received modules will keep the physical mapping correct.

      Commands List~

      The following SPM specific commands are supported.

      Command Parameters
      SspmDisplay Change GUI display between rotating display of all scanned relays, of all scanned relays that are powered on or select a group of four relays using a tab
      0 = Display all relays
      1 = Display powered on relays
      2 = Provide user selectable tabs to display four relays within a 4Relay module
      SspmEnergyTotal<x> (P)reset total energy in kWh without today's energy for relay <x>
      0 = preset with total of history
      0.01..262143.99 = set value in kWh
      SspmEnergyYesterday<x> (P)reset energy yesterday in kWh for relay <x>
      0.01..262143.99 = set value in kWh
      SspmHistory<x> Show daily energy for relay <x> of up to 180 days
      SspmIAmHere<x> Blink SPM-4Relay module error light containing relay <x>
      SspmLog<x> Show relay <x> power state change and cause
      SspmMap Map scanned SPM-4Relay modules to physical location. Use unique numbers for all fields
      0 = rescan modbus for SPM-4Relay modules and reset mapping to default. This takes at least 20 seconds
      3,4,1,2 = map scanned module 1 to physical module 3 containing relays 9 to 12, module 2 to 4 with relays 13 to 16, module 3 to 1 with relays 1 to 4 and module 4 to 2 with relays 5 to 8
      SspmOverload<x> Set overload detection criteria for relay <x>
      0 = reset and disable overload detection to scanned module criteria
      <delay>,<min_power>,<max_power>,<min_voltage>,<max_voltage>,<max_current> = set any or all overload detection criteria
      0,0.10,4400.00,0.10,240.00,20.00 = enable overload detection with default values
      9,0,22.1 = enable max_power over 22.1W detection after 9 seconds
      0,0,0,0,235.2 = enable immediate max_voltage over 235.2V detection
      SspmPowerOnState<x> Set v1.2.0 relay <x> power on state overruling Tasmota global power on state
      0 = off, 1 = on, 2 = saved state
      SspmScan Rescan modbus for SPM-4Relay modules. This takes at least 20 seconds
      \ No newline at end of file diff --git a/Status-LED/index.html b/Status-LED/index.html new file mode 100644 index 0000000000..42d8c23283 --- /dev/null +++ b/Status-LED/index.html @@ -0,0 +1,11 @@ + + + + + + + + +Redirecting... + + diff --git a/Subscribe-&-Unsubscribe/index.html b/Subscribe-&-Unsubscribe/index.html new file mode 100644 index 0000000000..fd04019eb4 --- /dev/null +++ b/Subscribe-&-Unsubscribe/index.html @@ -0,0 +1 @@ + Subscribe & Unsubscribe - Tasmota
      \ No newline at end of file diff --git a/Supported-Modules/index.html b/Supported-Modules/index.html new file mode 100644 index 0000000000..3d19663281 --- /dev/null +++ b/Supported-Modules/index.html @@ -0,0 +1 @@ + Supported Modules - Tasmota
      Skip to content
      \ No newline at end of file diff --git a/Supported-Peripherals/index.html b/Supported-Peripherals/index.html new file mode 100644 index 0000000000..0e681bf685 --- /dev/null +++ b/Supported-Peripherals/index.html @@ -0,0 +1 @@ + Peripherals - Tasmota
      Skip to content

      Supported Peripherals~

      Name Description Connection
      74x595 74x595 Shift Registers gpio
      A4988 Stepper Motor Controller
      ADC Analog input over A0 pin analog
      ADS111x A/D Converter I2C
      AHT1x Asair AHT10/AHT15 Temperature and Humidity Sensor I2C
      AHT2x Asair AHT20/AHT21/AHT25/AM2301B Temperature and Humidity Sensor I2C
      AM2301 / DHT21
      AM2302 / DHT22
      AM2321
      Temperature and Humidity Sensor gpio
      AM2301B Temperature and Humidity Sensor
      Use same driver as AHT2X
      I2C
      AM2320 Temperature and Humidity Sensor gpio
      AS608 AS608 Optical and R503 Capacitive Fingerprint Sensor serial
      AS3935 Franklin Lightning Sensor serial
      APDS-9960 Ambient Light, RGB Color and Proximity Sensor with Gesture Detection I2C
      AZ 7798 CO2 Meter Datalogger
      BH1750 Luminosity Sensor
      BMP280
      BMP085
      BMP180)
      Pressure Sensor
      BME280 Temperature, Humidity and Pressure Sensor I2C
      BME680
      BME688
      Temperature, Humidity, Pressure and Gas Sensor I2C
      Buzzer Audio Signalling Device gpio
      CC2530 TI CC2530 Zigbee Adapter serial
      CCS811 Gas and Air Quality Sensor I2C
      Chirp! Soil Moisture Sensor Moisture Sensor I2C
      DFR0299 DFRobot DFPlayer Mini MP3 Player serial
      DHT11 Temperature and Humidity Sensor gpio
      DHT12 Temperature and Humidity Sensor gpio
      DY-SV17F DY-SV17F MP3 Player serial
      DYP-ME007 Ultrasonic distance Sensor serial
      DS18x20 Temperature Sensor 1-Wire
      DS1624
      DS1621
      Temperature Sensor 1-Wire
      DS3231 Real-Time-Clock I2C
      DS3502 Digital potentiometer I2C
      EZO EZO series of chemical Sensors I2C
      F&F LE-01MR F&F LE-01MR Single Phase Modbus Energy meter
      GDK101 Gamma radiation sensor I2C
      GGreg20_V3 Ionizing Radiation Detector gpio (ESP32 only)
      GM861 Barcode/QR reader serial
      GPS-NTP-server GPS-NTP-server serial
      HDC1080 Texas Instruments HDC1080 Humidity and Temperature Sensor I2C
      HDC2010 Texas Instruments HDC2010 Humidity and Temperature Sensor I2C
      HM-10 BLE Bluetooth gateway serial
      HM-17
      HM-16
      Bluetooth iBeacon reader serial
      HMC5883L 3-channels Magnetic Field Sensor I2C
      HP303B Barometric Pressure Shield I2C
      HYTxx Temperature and Humidity Sensor I2C
      HR-E Water Meter Encoder interface serial
      HRG-15 Hydreon RG-15 Rain Gauge Sensor serial
      HRXL MaxBotix HRXL line of Sonar Ranging Sensors gpio
      HC-SR04
      HC-SR04P
      JSN-SR04T)
      Ultrasonic Sensor gpio
      HM330x SeedStudio Grove HM3301 Particulate Matter Sensor I2C
      HMC5883L 3-Axis Digital Compass sensor I2C
      HLK-LD2410 24GHz mmWave Presence Sensor serial
      Honeywell HIH Temperature and Humidity Sensor I2C
      Honeywell HPMA115xx Particulate Matter Sensor serial
      HTU21 Temperature and Humidity Sensor I2C
      HX711 Load Cell Sensor gpio
      HRG15 Solid State Rain Sensor serial
      I2S Audio I2S Audio DAC and microphone I2s
      iAQ-Core Indoor Air Quality Sensor I2C
      iEM3000 Schenider Electric modbus energy meter serial
      INA219
      ISL28022
      High-Side DC Current and Voltage Sensor I2C
      INA226 High-Side or Low-side DC Current and Voltage Sensor I2C
      IR Remote IR transmitter and receiver
      K30, K70, S8 SenseAir CO2 Sensors serial
      LM75AD Temperature Sensor I2C
      LMT01 Temperature Sensor gpio
      MAX17043 LiPo Fuel Gauge I2C
      MAX31855
      MAX6675
      Thermocouple Sensor SPI
      MAX31865 RTD Temperature Sensor Amplifier SPI
      MAX44009 Ambient Light Sensor I2C
      MCP23008
      MCP23017
      MCP23S17
      I/O Expander I2C
      MCP2515 CAN bus controller SPI
      MCP9808 Temperature Sensor I2C
      ME007 Ultrasonic Distance Sensor gpio
      MFRC522 NFC Tag Reader SPI
      MGC3130 3D Tracking and Gesture Controller
      MH-Z19B CO2 Sensor
      MLX90614 MLX9061X Infrared Thermometer I2C
      MLX90640 MLX90640 Far Infrared Thermal Sensor Array I2C
      MPR121 Proximity Capacitive Touch Sensor Controller (I2C)
      MPU6050 3-Axis Gyroscope and 3-Axis Accelerometer Sensor (I2C)
      MQ MQ Sensors (MQ-02, MQ-03, MQ-04, MQ-05, MQ-06, MQ-07, MQ-08, MQ-09, MQ-131, MQ-135) analog
      MS01 Moisture sensor from Sonoff 1wire
      NeoPool Sugar Valley NeoPool Controller serial
      NRF24L01 NRF24L01 as BLE-bridge for Mijia BT Sensors SPI
      OpenTherm OpenTherm adapter serial
      P1 Smart Meter Energy Meter serial
      PAJ7620 Gesture & Proximity Detection Sensor
      PIR Passive Infrared Sensor gpio
      PCA9557 I/O Expander I2C
      PCA9685 16-channel, 12-bit PWM LED controller I2C
      PCA9632 4-channel, 8-bit PWM LED controller I2C
      PCF8574(A) 8-port I/O Expander I2C
      PMS3003
      PMS5003
      PMS7003

      PMSx003T
      Particle Concentration Sensor serial
      PMSA003I Air quality sensor I2C
      PN532 NFC/RFID controller
      PZEM-004
      PZEM-016
      Energy Monitor serial
      QMC5883L Magnetic Field sensor I2C
      RCWL-0516 Microwave Radar Presence detection
      RDM6300 125Khz RFID Module
      RF Transceiver IR receiver and/or transmitter
      RX-4M50RR30SF
      RX-AM8SF
      RF Sensor receiver gpio
      SCD30 CO2 Sensor I2C
      SCD40
      SCD41
      CO2 Sensor I2C
      Eastron SDM72 Modbus Energy Meter serial
      Eastron SDM120 Modbus Energy Meter serial
      Eastron SDM220 Modbus Energy Meter serial
      Eastron SDM230 Modbus Energy Meter serial
      Eastron SDM630 Modbus Energy Meter serial
      YF-DN50 Flow rate meter gpio
      SDS011
      SDS021
      Laser Dust Sensor
      SEN0390 Ambient Light Sensor I2C
      SEN5X All-in-one Environmental Sensor I2C
      SGP30 Gas and Air Quality Sensor I2C
      SGP40 Gas and Air Quality Sensor I2C
      SGP41 VOC and NOx Sensor I2C
      SHT1x Temperature and Humidity Sensor I2C
      SHT30 Humidity & Temperature Sensor I2C
      SHT4x Temperature and Humidity Sensor I2C
      SI114x UV Index, IR and Visible Light Sensor I2C
      Si7021 Humidity and Temperature Sensor I2C
      SK6812 Addressable LEDs
      Smart Meter Interface Smart Meter Interface serial, gpio
      SolaX X1 SolaX Power X1 inverter serial
      SPS30 Particulate Matter PM)
      T6703
      T6713
      Telaire T6700 Series CO2 sensor I2C
      TC74 Temperature Sensor I2C
      Téléinfo French energy measuring system serial
      TFMini TFmini, TFmini Plus, TFmini Plus (Indoor Version), TFmini-S LiDAR module serial
      TM1638 8 Switch, LED and 7 Segment Unit Sensor gpio
      TSL2561 Luminosity Sensor I2C
      TSL2591 Luminosity Sensor I2C
      TX20
      TX23
      WS2300
      La Crosse TX2x / Technoline WS2300-15 Anemometer gpio
      VEML6070 UV Sensor I2C
      VEML6075 UVA/UVB/UVINDEX Sensor I2C
      VEML7700 Ambient light intensity Sensor I2C
      VL53L0x Time of flight Sensor I2C
      VL53L1x Time of flight Sensor I2C
      VINDRIKTNING IKEA VINDRIKTNING Particle Concentration Sensor serial
      WindMeter Analog cup anemometer
      WS2812B Wemos Shield with Addressable LED
      WS2812B
      WS2813B
      Addressable LEDs
      **Xadow Grove) Mutichannel Gas Sensor** gas Sensor (I2C

      Google Sheet list of supported peripherals

      Sensor API Documentation

      Expanding Specific Devices~

      \ No newline at end of file diff --git a/TFL/index.html b/TFL/index.html new file mode 100644 index 0000000000..14d0e7ec93 --- /dev/null +++ b/TFL/index.html @@ -0,0 +1,217 @@ + TensorFlow Lite - Tasmota
      Skip to content

      TensorFlow Lite ~

      This feature is not included in precompiled binaries

      add as a build flag to your build environment in platformio_tasmota_cenv.ini:

      build_flags             = ${env:tasmota32_base.build_flags}
      +                        -DUSE_BERRY_TF_LITE
      +                        -DESP_NN ; use optimized kernels for ESP32
      +                        -DUSE_I2S ; add only for speech/microphone use
      +lib_extra_dirs          = lib/libesp32, lib/libesp32_div, lib/lib_basic, lib/lib_i2c, lib/lib_div, lib/lib_ssl, lib/libesp32_ml
      +; lib/libesp32_ml is important to include 
      +

      Different Levels of TensorFlow~

      TensorFlow is an open-source machine-learning platform that is widely adopted and thus is a whole ecosystem with tools, libraries and a huge community. It is not application specific and can be used for all kind of tasks.
      Initially it was only usable on fully fledged computers but over time technical advances in software and hardware made it possible to step down the stairs and make it usable on weaker devices.
      So the next smaller thing is called TensorFlow Lite that was targeted for smaller devices like the famous ARM-based 'RaspberryPi'.

      But - you guessed it - this was not the end of the line and the whole armada of microcontrollers reached out to be part of the machine learning world. The name of this stripped down platform is TensorFlow Lite for Microcontrollers.
      Tasmota uses a fork from Espressif with optimized functions for maximal performance.

      Machine Learning ... is what exactly again?~

      Machine learning, deep learning, KI, AI, ... - these are very blurry defined words, that mean more or less the same and scale from pretty complex up to extremely complex software constructs. Let's try to have a very simplified look at it.

      In ancient times (like 20 years ago) problems where solved on computers by finding an algorithm to compute input data and thus getting output data.
      With machine learning the process is still to have input data, but to use known good output data to let a piece of software find the algorithm to get from the known input data to the known output data - and hope that this still works with unknown input data.
      The mixture of algorithms and found parameters will be put into a structure which is called a model and the process of building it is usually named training.
      The model is created programmatically, although this process can be hidden behind a GUI and at this stage the number of inputs and outputs will be defined.
      Although it would be technically possible to feed raw sensor data into the model, this is not how it is done in most cases. The very common step in between is called feature extraction, which reduces the amount of data and often the dimensionality to make the whole process more efficient and thus even possible for some applications. A good feature extraction will help to later run a successful inference and there are tools to show, how different input entities form clusters of feature data.
      After feeding the model with sensible data it is time to press a virtual "go button" and invoke an inference, that does the magic computations inside the model.
      After that the outputs will get populated with some values, that have to be interpreted as probabilities. It can be required to add some complex logic in order interpret this data or do some averaging of real-time data.

      The processed data is historically typically in the floating point format, but for weak devices like MCU's it is a very common thing to shrink the 4 bytes of a float32 to 1 byte of an int8 by quantization. This happens on various places and will lead to a loss of precision, but is very often nearly on par in the final result.

      Design Considerations for Tasmota~

      The understandable way to optimize machine learning applications for MCU's is to build and later flash one monolithic firmware, where everything has to be done at/before compile time. This makes sense and is fundamentally the only way to quench out every little bit of performance and memory optimization.

      Nonetheless for Tasmota we do not really want to build one-trick-pony firmwares and the idea is to be able to run all kinds of TensorFlow applications with one firmware, that has enough generic capabilities to dynamically load models and extra code at runtime. On the other hand we can optimize the program flow for the ESP chips and Free-RTOS.
      The API shall be quite slim with as few boilerplate code as possible, but can be extended as needed and use Berry to add additional logic in the first place.

      Accelerate your development workflow for machine learning

      Usually developing machine learning applications on MCU's means that every small change to the model and/or application code will need a firmware compilation and flashing.
      With Tasmota this is now independent to each other. You can retrain and reupload the model to the file system as often as needed at runtime. The same is valid for refining the Berry code.

      In order to get things done like speech recognition, where especially realtime feature extraction is not possible to achieve with Berry (with the knowledge of today), additional specialized functionality is included.
      Where it makes sense helper functions for capturing data are added too.

      Before you proceed, check you expectations!!

      The most important prerequisite for speech recognition on MCU's is to have the right mindset. It is totally impossible to reach the level of recognition of a commercial product (Alexa, Siri, Cortana, ... you name it) on much stronger hardware. The usual cheap I2S MEMS microphone alone is a severe problem and the tuning of the various variables of the model is trial and error in many cases. It will make the final application fail with wrong but similar sounding words or mumbling.
      Be happy if it works and just don't do it, if you can not handle frustration! You will have to invest some time to fine tune every specific application!

      This sums up to these steps:

      1. Create a TensorFlow lite model (including collecting data, finding good parameters, final training). Use Edgeimpulse to make this easy or create your own training pipeline.

      2. Create a Berry application (loading the model, running inference, interpret the result, acting on the result). Use building blocks from this page to get started.

      Speech Recognition~

      For the specific task of speech recognition a big hurdle lies in the way in the form of "How to create a model"?. Although there are numerous ways published on various places, that sound quite similar to each other, this is a very non-trivial task.
      While the same principles apply to all examples that you may find, the implementation differences under the hood are quite substantial.
      The hardest part here is to do the feature extraction of the audio data in a compatible(!!!) way for training and inference on the device. Usually the raw audio data is processed using mel frequency conversion, FFT and some kind of logarithm. You may find words like power spectrum, mel frequency energies and mel frequency cepstral coefficients, which sadly are very implementation specific and not interchangeable.
      So it is utterly important to use the same (= compatible) implementations for the whole pipeline of Training->Deployment->Usage.

      For convenience reasons and after extended testing it was decided to be compatible with Speechpy (with small differences). Thus it is possible to use Edge Impulse as the training platform for model creation, which uses Speechpy too and is a development platform for machine learning on small devices, that shares some similarities to GitHub with regards to costs. Everything that we need for Tasmota is free, the enterprise solution they offer for money is not needed.
      Technically it is definitely possible to build a complete offline training pipeline as basically everything on Edgeimpulse is open source and can be recreated by a motivated user for local usage.

      If you use Edge Impulse for online training, you do not have to create a new firmware image for your Tasmota device. Instead of choosing one of the deployment options from Edge Impulse, you only have to download the binary TensorFlow Lite model (usually the int8 quantized version) from the Dashboard, give it a suitable name and upload it to Tasmotas filesystem in order to use it with Berry afterwards.

      Example - speech recognition using MFCC:

      Feature parameters

      Usage~

      Functionality is provided by the module TFL, so always start with import TFL.

      TFL Function Parameters and details
      begin (type:string, descriptor:bytes)
      First step creates an internal context of type
      "BUF" - generic session with arbitrary byte buffers for input and output
      "MIC" - for voice recognition and capturing training data
      "CAM" - not implemented yet
      "" - stops a running session, can be used to init the internal log buffer
      `descriptora byte array with specific meaning for every type
      load (model:bytes, output:bytes, memory:int)
      Will load and run the model
      model a bytes buffer that holds the model data for the entire lifecycle
      output a byte buffer that matches the output size of the model
      memory optional (default: 2000), a model specific memory size in bytes that will be allocated, find by trial-end-error
      input (input:bytes)
      Feed the input of the model with values, must match the size of the model input. Will invoke an inference.
      output (output:bytes) -> bool
      Returns true if function was not called yet on actual output data.
      rec (filename:string, seconds:int)
      Records audio to a WAV file (Mono, 16 kHz, 16 Bit).
      stats () -> string
      Returns a JSON string with some metrics of the current session.
      log () -> string
      Returns a log message, typically for debugging.

      Use Netron to check your model

      https://netron.app shows the inner structure of a tensorflow model including format and size of the inputs and outputs. Online and offline versions of the application available for every major platform.

      "Hello World" Sort Of...~

      Perhaps THE starter project is the in itself pretty useless "sinewave example", which is nicely explained in these videos.

      Of course we can run this model perfectly fine inside Tasmota, with a Berry code snippet:

      import TFL
      +TFL.begin("BUF")  # generic TFL session 
      +var i = bytes(-4) # holds one float, size must match the model
      +var o = bytes(-4) # dito
      +
      +var model = open("sine_model.tflite").readbytes() # this binary file must have been saved in the FS before
      +TFL.load(model,o,3000) # load and run
      +
      +var count = 0.0
      +def cb() 
      +    if TFL.output(o)
      +        # "draw" a bar
      +        var v =  int((o.getfloat(0) + 1) * 15)
      +        var bar_el = "="
      +        var bar = ""
      +        for i:0..v bar+=bar_el end
      +        print(bar)
      +
      +        # or print the value
      +        # print(o.getfloat(0))
      +
      +        # prepare new input in the range of 0..6
      +        count+= 0.1 
      +        if count>6 count=0 end
      +        i.setfloat(0,count) 
      +        TFL.input(i) # and feed it into the model
      +    end
      +    var s = TFL.log() # receive log messages from the TF lite tasks
      +    if s print(s) end 
      +    tasmota.set_timer(50, cb) 
      +end
      +cb()
      +
      +# initiate loop 
      +TFL.input(i)
      +

      This will pollute your Tasmota console with a moving bar resembling a sine wave.

      In fact this shows a very generic application, where the format and size as well as the meaning of the in- and outputs must be known, but the model itself can remain a "black box". We do not need to know, which kernels or parameters are used.

      Use 'TFL.stats()' to find memory size

      The metrics will show the used bytes of the model. So start with a large value and tune down, but leave a margin above the number in the output of the command.

      Speech Recognition / Keyword Spotting~

      Training can be done entirely with Edgeimpulse. See their documentation for Responding to your voice.

      It is important to have a basic understanding of the process of speech recognition in order to set and tune the configuration:
      An audio stream is processed in small slices, which have a duration and a stride. They can overlap or run one after the other.

      Tasmota will process every audio slice and immediately extract the features with support for the modes: MFE and MFCC.
      For performance reasons, we do not support every setting Edgeimpulse provides. Do not use Window size for MFCC mode by setting it to 0. More on this later.

      We use the following fixed audio settings:

      Setting Value
      Sample length 1 second - means the word to train should be shorter (you might get away with training a shorter part of a longer word)
      Sample frequency 16kHz - a proven compromise of sound quality, that is just enough for voice recognition, but can be handled by the ESP32
      Sample format 16 bit, mono
      Low frequency 300 Hz - default in Edgeimpulse
      High frequency 8000 Hz - Sample frequency/2 and default too. Higher values would not make sense because of the NYQUIST theorem.

      Create Training Data~

      A working microphone setup for recording training data and later audio recognition is needed.

      Therefor the -DUSE_I2S must be defined in the build_flags: section of the environment in platformio_tasmota_cenv.ini. This allows to set the GPIO pins for the I2S microphone according to I2S-Audio.

      Although you can use the microphone of your computer or smartphone, it is highly recommended to use your ESP32 microphone at least for a large amount of samples in order to train the model with the same pure sound quality, that it later has to run inference on. A working SD card config is the best way to capture training data.

      You can use a simple Berry driver to get this done:

      # record to 16-bit WAV file at 16 kHz, SD card highly recommended
      +# repeat the intended keyword roughly every second in the same technical setup, that will later be used for keyword spotting
      +# usage 'rec<seconds> filename', example: rec30 left -> record 30 seconds from I2S microphone to '/left.wav'
      +class WAV_REC : Driver
      +    var secs
      +
      +    def init()
      +        import TFL
      +        TFL.begin("") # init empty for log buffer
      +        print("TFL: command 'rec' added")
      +        self.secs = 0
      +    end
      +
      +    def begin()
      +        import TFL
      +        var descriptor = bytes(-10) # mic descriptor
      +        descriptor[0] = 4  # i2s_channel_fmt, 4=left
      +        descriptor[1] = 16 # amplification factor
      +        descriptor[2] = 32 # slice_dur
      +        descriptor[3] = 32 # slice_stride
      +        descriptor[4] = 40 # mfe filter (= features if MFE mode)
      +        descriptor[5] = 0  # mfcc coefficients, if 0 -> compute MFE only
      +        descriptor[6] = 9  # 2^9 = 512 fft_bins
      +        descriptor[7] = 10 # max. invocations per second
      +        descriptor[8] = 52 # db noisefloor -> negative value
      +        descriptor[9] = 0  # preemphasis
      +        TFL.begin("MIC",descriptor)
      +    end
      +
      +    def every_second()
      +        if self.secs > 0
      +            print(self.secs)
      +            if self.secs == 1 print("... done!") end
      +            self.secs -= 1
      +        end
      +    end
      +
      +    def every_100ms()
      +        import TFL
      +        var s = TFL.log()
      +        if s print(s) end
      +    end
      +
      +    def rec(name,secs)
      +        import TFL
      +        self.begin()
      +        TFL.rec(name,secs) # Edgeimpulse can infer the label from the name, so name it accordingly
      +        self.secs = secs + 1 # add startup latency
      +        print("TFL: record",secs,"seconds to",name)
      +    end
      +end
      +
      +var wav_rec = WAV_REC()
      +tasmota.add_driver(wav_rec)
      +
      +def rec(cmd, idx, payload, payload_json)
      +    var file = "/" + payload + ".wav"
      +    wav_rec.rec(file,idx)
      +    tasmota.resp_cmnd_str(file)
      +end
      +
      +tasmota.add_cmd('rec', rec) # rec30 left -> record 30 seconds from I2S microphone to "/left.wav"
      +

      Most of the descriptor values do not really matter here, but the whole descriptor must have valid values, meaning some values MUST not be 0!

      Then you can talk into the microphone with around 1 word per second and later upload this file to your Edge Impulse project via your computer.

      General strategies~

      1. Mix input samples from different microphones. Using the computer or phone for audio recording is way more user friendly, so add some samples the easy way. But without enough samples from your ESP32 microphone the final performance will be mediocre at best.
      2. Input samples from different speakers will very likely lead to a more robust model. So ask family and friends for help.
      3. More is more. Almost always adding more samples will result in a better performing model. Don't give up, if testing performance degrades in the early stages of audio harvesting. Just make sure to only add valid audio data with correct labels and the final result will improve with more audio data.

      Edge Impulse~

      For training on Edge Impulse some post processing is usually necessary.

      If you have uploaded a sample containing many repetitions of one word, there is a convenient split function, that slices the long mega-sample into one-second samples. For very short words this works automatically pretty well, for longer words close to one second it is recommended to check and fix possible errors immediately.
      You can check the resulting simple sample including the correct label:

      16-bit-sample with label "computer":

      Final sample

      The feature extraction step needs some parameters and will happen in the same way before training and later on-device.

      MFCC parameters:

      Feature parameters

      This will create a feature array, which will stack the features of every slice on each other. This will be visualized as a heat map, where a slice is a column (oldest left) and the lines represent the frequency banks. Thus this "picture" holds the data of one second of audio, that the ML model will later "see" to do inference.

      MFCC features for 1 second of audio:

      Feature map

      Configuring the neural network architecture can be done in a GUI. Following the rule to go deep (more layers) and not too wide (less neurons) results in smaller models.

      "Deep" model for 7 keywords (plus unknown and noise) with size of only 14 kB:

      Neural network architecture

      After training and configuring the model on Edgeimpulse we can simply download the TensorFlow Lite model from the dashboard (the int8 version) and upload it to the ESP32 filesystem.

      Tasmota Side~

      On the Tasmota side the feature settings have to be translated in Berry with a descriptor. The descriptor settings may change in the future!

      number property value
      0 i2s_channel_fmt_t i2s_channel_fmt_t from i2s_types.h, we need mono, so 3 for right or 4 for left
      1 amplification factor typical values are 8 - 32, must be >0, too high values will lead to clipping
      The audio output of most I2S microphone is very low. Check your setup by recording a WAV file and analyze in Edgeimpulse or use an audio tool like Audacity.
      2 slice duration
      or Frame length
      A slice of audio data in milliseconds. With the fixed audio settings 32 milliseconds translate to 512 samples, which as a power of 2 is a good fit for FFT.
      3 slice stride
      or Frame stride
      Time in milliseconds between the starts of 2 adjacent slices. No overlap happens if duration and stride are equal. Lower values create more stress for the system. Values around and below 20 milliseconds may leads to problems. Bad Wifi will worsen things earlier!!
      4 Filter number Number of captured mel energies. For MFE mode these are the feature values for one slice.
      5 MFCC coefficients
      or Number of coefficients
      If not 0, this means we use MFCC mode and these are the feature values for one slice.
      6 FFT size
      or FFT length
      In powers of 2. For now must be equal or greater than sample size, means slice duration of 32 ms -> sample size 512 -> 2^9 -> 9
      7 Inferences per second Typical values 4-10. More values need more averaging in post-processing, but will likely show less missed words.
      8 Noisefloor In dB for MFE mode. Sound data with level below that threshold will be discarded.
      9 preemphassis Factor will be divided by 100, so 98 -> 0.98. It is recommended to not use this, as Edgeimpulse and Speechpy compute this over the whole sample, while Tasmota does this per slice, which is way more efficient but creates a difference. There is a way to overcome this. More later ...

      The Berry driver for the speech recognition must be adapted for every model and use case. A plain old ESP32 without extra PS-RAM is enough to "hear" with about 10 inferences per per second.

      Demo example:

      # globals
      +var key_words = ["down", "left", "noise", "right", "up"] # must match your trained model!! Edgeimpulse sorts them alphabetically
      +var thresholds = [700,710,680,680,720] # same indices as key_words, increase against false positives, decrease against misses, 
      +                                       # max. theoretical value = 256 * number of averages for int8_t output tensor
      +
      +class PREDICTOR
      +    var avgs, sz, time_out
      +    var out_buf, out_buf_idx
      +
      +    def init(sz, averages)
      +        self.out_buf = bytes(-(averages * sz)) # creates circular buffer for averaging
      +        self.out_buf_idx = 0 
      +        self.time_out = 0 # after successful word finding, make a break to avoid "noise findings"
      +        self.sz = sz # size of word list/output tensor
      +        self.avgs = averages # number of averaging/oversampling steps
      +    end
      +
      +    def clear() # clear oversampling buffer after word was found
      +        for i:0..size(self.out_buf)-1
      +            self.out_buf[i] = 0
      +        end
      +    end
      +
      +    def run(data)
      +        if self.time_out > 0
      +            self.time_out -= 1
      +            return 255 # return some error code = no result
      +        end
      +
      +        for i:0..(self.sz-1)
      +            self.out_buf[self.out_buf_idx+i] = data[i]
      +        end
      +        self.out_buf_idx += self.sz
      +        if self.out_buf_idx>(self.avgs-1) * self.sz
      +            self.out_buf_idx = 0 
      +        end
      +
      +        var average
      +        for i:0..(self.sz-1)
      +            average = 0
      +            for j:0..self.avgs-1
      +                average +=  self.out_buf[i+(j*self.sz)]
      +            end
      +            # if i == 4
      +            #     print(average) # test the threshold for single words while developing, needed in the next step
      +            # end
      +            if average > thresholds[i] # find the threshold stored in the global list by trial and error
      +                if i != 2 # this is the non-noise/unknown output - you must infer this from your model
      +                    self.clear() # only clear the buffer after finding a keyword, not for noise/unknown
      +                    self.time_out = 10 # find the value by trial and error - good starting point is one second
      +                                       # lower number allows faster speech, but might increase false positives
      +                    print(average)
      +                end
      +                return i
      +            end
      +        end
      +        return 255 # return some error code = no result
      +    end
      +end
      +
      +class MICROSPEECH : Driver
      +    var o, o_sz  # output tensor, size of output tensor
      +    var p        # predictor instance
      +    var model    # this var really holds the model data for the entire session
      +    var new_out  # current output sensor converted to uint8_t
      +
      +    def init()
      +        import TFL
      +        self.o_sz = size(key_words) # this value must be correctly taken from the model/training pipeline
      +        self.new_out = []
      +        self.new_out.resize(self.o_sz)
      +        self.o = bytes(-self.o_sz) # size must match the model
      +        self.p = PREDICTOR(self.o_sz,4) # second arg is number of averages for prediction
      +
      +        var descriptor = bytes(-10)
      +        descriptor[0] = 4  # i2s_channel_fmt, 4=left
      +        descriptor[1] = 16 # amplification factor
      +        descriptor[2] = 32 # slice_dur in ms 
      +        descriptor[3] = 32 # slice_stride in ms
      +        descriptor[4] = 26 # mfe filter (= features if MFE mode)
      +        descriptor[5] = 13 # mfcc coefficients, if 0 -> compute MFE only
      +        descriptor[6] = 9  # 2^9 = 512 fft_bins
      +        descriptor[7] = 10 # max. invocations per second - find best value by testing on device
      +        descriptor[8] = 52 # db noisefloor -> negative value
      +        descriptor[9] = 0 # preemphasis
      +        TFL.begin("MIC",descriptor)
      +        self.model = open("mfcc.lite").readbytes() # file must be present on the ESP
      +        if self.model
      +            TFL.load(self.model,self.o,28000)
      +        end
      +    end
      +
      +    def every_50ms()
      +        import TFL
      +        if TFL.output(self.o)
      +            for i:0..(self.o_sz-1)
      +                self.new_out[i] = self.o.geti(i,1) + 128
      +            end
      +            #print(self.new_out)
      +            var r = self.p.run(self.new_out)
      +            if r != 2 && r != 255 # in this example not noise/unknown/error code - can be more complex
      +                print(key_words[r])
      +                # do something useful
      +            end
      +        end
      +        var s = TFL.log()
      +        if s print(s) end
      +    end
      +end
      +
      +var mic = MICROSPEECH()
      +
      +tasmota.add_driver(mic)
      +

      \ No newline at end of file diff --git a/TLS/index.html b/TLS/index.html new file mode 100644 index 0000000000..316bf73556 --- /dev/null +++ b/TLS/index.html @@ -0,0 +1,13 @@ + TLS Secured MQTT - Tasmota
      Skip to content

      TLS Secured MQTT~

      This feature is included only in tasmota32 and tasmota-zbbridge binaries

      Starting with version 10.0.0.4, TLS now support dual mode, depending of the value of SetOption132:

      • SetOption132 0 (default): the server's identity is checked against pre-defined Certificate Authorities. There is no further configuration needed. Tasmota includes the following CAs:
      • LetsEncrypt R3 certificate, RSA 2048 bits SHA 256, valid until 20250915
      • Amazon Root CA, RSA 2048 bits SHA 256, valid until 20380117, used by AWS IoT
      • SetOption132 1: Fingerprint validation. This method works for any server certificate, including self-signed certificates. The server's public key is hashed into a fingerprint and compared to a pre-recorded value. This method is more universal but requires an additional configuration (see below)

      There is no performance difference between both modes.

      Fingerprint Validation~

      The fingerprint is now calculated on the server's Public Key and no longer on its Certificate. The good news is that Public Keys tend to change far less often than certificates, i.e. LetsEncrypt triggers a certificate renewal every 3 months, the Public Key fingerprint will not change after a certificate renewal. The bad news is that there is no openssl command to retrieve the server's Public Key fingerprint.

      The original Fingerprint V1 algorithm had a security potential vulnerability, it has been replaced by a new more robust method v2. To avoid breaking compatibility, Tasmota will automatically detect when a fingerprint v1 is present and will convert it automatically to V2.

      Important: the following tool to calculate it from your certificate is now deprecated. The fingerprint will work once and will be replaced with the new fingerprint.

      So to simplify your task, we have added two more options: 1/ auto-learning of the fingerprint, 2/ disabling of the fingerprint validation altogether.

      Option 1: Fingerprint auto-learn~

      If set, Tasmota will automatically learn the fingerprint during the first connection and will set the Fingerprint settings to the target fingerprint. To do so, use one of the following commands:

      #define MQTT_FINGERPRINT1      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
      +
      or
      #define MQTT_FINGERPRINT2      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
      +

      Option 2: Disable Fingerprint~

      You can completely disable server fingerprint validation, which means that Tasmota will not check the server's identity. This also means that your traffic can possibly be intercepted and read/changed, so this option should only be used on trusted networks, i.e. with an MQTT on your local network. YOU HAVE BEEN WARNED!

      To do so, set one of the Fingerprints to all 0xFF:

      #define MQTT_FINGERPRINT1      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
      +
      Tasmota also provide an option to authenticate clients using an x509 certificate and a public key for authentication, instead of username/password.

      For details on how to set up your local instance of Mosquitto, check the article Self-signed-Mosquitto.

      Compiling TLS for ESP8266~

      To use it you must compile your build. Add the following to user_config_override.h:

      #ifndef USE_MQTT_TLS 
      +#define USE_MQTT_TLS                             // Use TLS for MQTT connection (+34.5k code, +7.0k mem and +4.8k additional during connection handshake)
      +#define MQTT_TLS_ENABLED       true              // [SetOption103] Enable TLS mode (requires TLS version)
      +//  #define USE_MQTT_TLS_CA_CERT                   // Force full CA validation instead of fingerprints, slower, but simpler to use.  (+2.2k code, +1.9k mem during connection handshake)
      +                                                   // This includes the LetsEncrypt CA in tasmota_ca.ino for verifying server certificates
      +//  #define USE_MQTT_TLS_FORCE_EC_CIPHER           // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem)
      +#endif
      +

      Change port to 8883~

      #define MQTT_PORT              8883              // [MqttPort] MQTT port (10123 on CloudMQTT)
      +

      Ensure that for the environment you have selected, lib/lib_ssl is included on platformio_tasmota_env.ini :~

      lib_extra_dirs          = lib/lib_ssl
      +

      TLS offers increased security between your connected devices and your MQTT server, providing server authentication and encryption. Please refer to the general discussion in Securing-your-IoT-from-hacking

      Starting version 6.5.0.15, there are major changes to TLS to make it lighter in memory and easier to use. It has now reduced flash and memory requirements that makes it compatible with Web and Hue Emulation.

      If you are upgrading from a previous TLS activated version, there are breaking changes in the way Fingerprints are calculated

      At the Tasmota configuration, you need to enable to use the TLS Version. This is done by enable #define USE_MQTT_TLS in user_config_override.h and

      If you are using LetsEncrypt to generate your server certificates, you should activate #define USE_MQTT_TLS_CA_CERT. Tasmota will transparently check the server's certificate with LetsEncrypt CA. If you are generating self-signed certificates or prefer fingerprints, read below.

      Limitations~

      On ESP8266, starting with 6.5.0.15, AxTLS has been replaced with BearSSL. This uses less memory, typically 6.0k constantly, and an additional 6.8k during TLS connection.

      On ESP32, BearSSL provides a much lighter footprint than MbedTLS (~45kB instead of ~150kB) and continues to be used by Tasmota.

      Main limitations are:

      • Your SSL/TLS server must support TLS 1.2 and the ECDHE_ cipher - which is the case with the default Mosquitto configuration
      • The server certificate must have an RSA private key (max 2048 bits) and the certificate must be signed with RSA and SHA256 hash. This is the case with default LetsEncrypt certificates. ESP32 supports by default RSA private keys up to 4096 bits, ESP8266 must be compiled with option -DUSE_4K_RSA to support 4096 private keys.
      • Your SSL/TLS should support TLS 1.2 MFLN to limit buffer to 1024 bytes. If MFLN is not supported, it will still work well, as long as the server does not send any message above 1024 bytes. On ESP32 buffers are raised to 2048 bytes.
      • If you are using certbot with Letsencrypt: starting with v2.0.0 certbot requests ECDSA certificates by default. Make sure you explicitly add --key-type rsa and --rsa-key-size 2048 (or --rsa-key-size 4096).

      Implementation Notes~

      Arduino Core switched from AxTLS to BearSSL in 2.4.2, allowing further optimization of the TLS library footprint. BearSSL is designed for compactness, both in code size and memory requirements. Furthermore it is modular and allows for inclusion of only the code necessary for the subset of crypto-algorithms you want to support.

      All numbers below are for ESP8266

      Thanks to BearSSL's compactness and aggressive optimization, the minimal TLS configuration requires just 34.5k of Flash and 6.7k of Memory. The full-blown AWS IoT version with full certificate validation requires 48.3k of Flash and 9.4k of Memory.

      Here are the tips and tricks used to reduce Flash and Memory:

      • MFLN (Maximum Fragment Length Negotiation): TLS normally uses 16k buffers for send and receive. 32k looks very small on a server, but immensely huge for ESP8266. TLS 1.2 introduced MFLN, which allows the TLS Client to reduce both buffers down to 512 bytes. MFLN is not widely supported yet, but it is by recent OpenSSL versions and by AWS IoT. This is a huge improvement in memory footprint. If your server does not support MFLN, it will still work as long as the messages sent by the server do not exceed the buffer length. In Tasmota the buffer length is 1024 bytes for send buffer and 1024 bytes for receive buffer. Going below creates message fragmentation and much longer TLS connection times (above 3s). If your server does not support MFLN, you'll see a message to that effect in the logs.
      • Max Certificate size: BearSSL normally supports server certificates of up to RSA 4096 bits and EC 521 bits. These certificates are very uncommon currently. To save extra memory, the included BearSSL library is trimmed down to maximum RSA 2048 bit certificate and EC 256 bit certificate. This should not have any impact for you.

      Tasmota will crash if the server serves a 4096 bit RSA certificate. The crash will likely be in br_rsa_i15_pkcs1_vrfy. Enable USE_4K_RSA to avoid this behaviour.

      • EC private key: AWS IoT requires the client to authenticate with its own Private Key and Certificate. By default AWS IoT will generate an RSA 2048 bit private key. In Tasmota, we moved to an EC (Elliptic Curve) Private Key of 256 bits. EC keys are much smaller, and handshake is significantly faster. Note: the key being 256 bits does not mean it's less secure than RSA 2048, it's actually the opposite.
      • Single Cipher: to reduce code size, we only support a single TLS cipher and embed only the code strictly necessary. When using TLS (e.g. LetsEncrypt on Mosquitto) the supported cipher is ECDHE_RSA_WITH_AES_128_GCM_SHA256. Additionally, ECDHE offers Perfect Forward Secrecy which means extra security.
      • Adaptive Thunk Stack: BearSSL does not allocate memory on its own. It's either the caller's responsibility or memory is taken on the Stack. Stack usage can go above 5k, more than the ESP8266 stack. Arduino created a Thunk Stack, a secondary stack of 5.6k, allocated on Heap, and activated when a TLS connection is active. Actually the stack is mostly used during TLS handshake, and much less memory is required during TLS message processing. Tasmota only allocates the Thunk Stack during TLS handshake and switches back to the normal Stack afterwards. See below for details of actual memory usage.
      • Keys and CA in PROGMEM: BearSSL was adapted from original source code to push most on the tables and static data into PROGMEM: https://github.com/earlephilhower/bearssl-esp8266. Additional work now allows us to put the Client Private Key, Certificate and CA in PROGMEM too, saving at least 3k of Memory.

      Memory Usage~

      TLS on Tasmota has been aggressively optimized to use as little memory (heap) as possible. It was also optimized to limit code size.

      Memory consumption (nominal):

      • BearSSL lib: 1424 bytes (or 1024 bytes with LetsEncrypt or regular TLS)
      • BearSSL ClientContext: 3440 bytes
      • Buffers (1024 bytes in + 1024 bytes out + overhead): 2528 bytes
      • Total = 7.4k (or 7.0k with LetsEncrypt or regular TLS)

      Note: if you use USE_WEBSERVER, your impact is lowered by 2k since the Web log buffer is reduced from 4k to 2k. Overall, when activating USE_WEBSERVER, you just see a memory impact of 5.4k.

      Memory needed during connection (TLS handshake - fingerprint validation):

      • ThunkStack = 5308 bytes (or 3608 bytes with LetsEncrypt or regular TLS)
      • DecoderContext = 1152 bytes
      • Total for connection = 6.5k (or 4.8k with LetsEncrypt or regular TLS)

      Memory needed during connection (TLS handshake - full CA validation):

      • ThunkStack = 5308 bytes (or 3608 bytes with LetsEncrypt or regular TLS)
      • DecoderContext = 3072 bytes
      • Total for connection = 8.4k (or 6.7k with LetsEncrypt or regular TLS)

      Connection Time~

      ESP8266 is quite slow compared to modern processors when it comes to SSL handshakes. Here are observed performance times when connecting to an SSL/TLS server, depending on CPU frequency (80MHz or 160MHz):

      AWS IoT Connection, with EC Private Key, simple fingerprint validation:

      • 0.7s at 160MHz
      • 1.3s at 80 MHz

      AWS IoT Connection, with EC Private Key, full CA validation (easier to configure than fingerprints):

      • 1.0s at 160MHz
      • 1.8s at 80 MHz

      LetsEncrypt based server (Mosquitto for ex), simple fingerprint validation:

      • 0.3s at 160MHz
      • 0.4s at 80MHz

      LetsEncrypt based server (Mosquitto for ex), with full CA validation (easier to configure than fingerprint):

      • 0.4s at 160MHz
      • 0.7s at 80MHz

      TLS Troubleshooting~

      Here are most common TLS errors:

      Error code Description
      -1004 Missing CA
      -1003 Missing EC private key
      -1002 Cannot connect to TCP port
      -1001 Cannot resolve IP address
      -1000 Out of memory error
      1 Bad fingerprint
      2  BR_ERR_BAD_STATE
      3 BR_ERR_UNSUPPORTED_VERSION
      4  BR_ERR_BAD_VERSION
      5  BR_ERR_BAD_LENGTH
      6  BR_ERR_TOO_LARGE
      7  BR_ERR_BAD_MAC
      8 BR_ERR_NO_RANDOM
      9 BR_ERR_UNKNOWN_TYPE 
      10 BR_ERR_UNEXPECTED
      12 BR_ERR_BAD_CCS
      13  BR_ERR_BAD_ALERT
      14 BR_ERR_BAD_HANDSHAKE
      15  BR_ERR_OVERSIZED_ID
      16  BR_ERR_BAD_CIPHER_SUITE
      17 BR_ERR_BAD_COMPRESSION
      18  BR_ERR_BAD_FRAGLEN
      19 BR_ERR_BAD_SECRENEG
      20  BR_ERR_EXTRA_EXTENSION
      21 BR_ERR_BAD_SNI
      22 BR_ERR_BAD_HELLO_DONE 
      23 BR_ERR_LIMIT_EXCEEDED: the server's public key is too large. Tasmota TLS is limited to 2048 RSA keys
      24  BR_ERR_BAD_FINISHED 
      25  BR_ERR_RESUME_MISMATCH
      26  BR_ERR_INVALID_ALGORITHM
      27  BR_ERR_BAD_SIGNATURE
      28 BR_ERR_WRONG_KEY_USAGE
      29 BR_ERR_NO_CLIENT_AUTH
      31 BR_ERR_IO
      54 BR_ERR_X509_EXPIRED X.509 status: certificate is expired or not yet valid.
      56 BR_ERR_X509_BAD_SERVER_NAME X.509 status: expected server name was not found in the chain.
      62 X509 not trusted, the server certificate is not signed by the CA (AWS IoT or LetsEncrypt)
      266 SSL3_ALERT_UNEXPECTED_MESSAGE
      276 TLS1_ALERT_BAD_RECORD_MAC
      277  TLS1_ALERT_DECRYPTION_FAILED
      278 TLS1_ALERT_RECORD_OVERFLOW
      286 SSL3_ALERT_DECOMPRESSION_FAIL
      296 SSL3_ALERT_HANDSHAKE_FAILURE
      298 TLS1_ALERT_BAD_CERTIFICATE: Missing or bad client private key
      299 TLS1_ALERT_UNSUPPORTED_CERT
      300  TLS1_ALERT_CERTIFICATE_REVOKED
      301  TLS1_ALERT_CERTIFICATE_EXPIRED
      302  TLS1_ALERT_CERTIFICATE_UNKNOWN
      303  SSL3_ALERT_ILLEGAL_PARAMETER
      304  TLS1_ALERT_UNKNOWN_CA 
      305 TLS1_ALERT_ACCESS_DENIED
      306  TLS1_ALERT_DECODE_ERROR
      307 TLS1_ALERT_DECRYPT_ERROR
      316  TLS1_ALERT_EXPORT_RESTRICTION
      326  TLS1_ALERT_PROTOCOL_VERSION
      327  TLS1_ALERT_INSUFFIENT_SECURITY
      336  TLS1_ALERT_INTERNAL_ERROR
      346  TLS1_ALERT_USER_CANCELED
      356 TLS1_ALERT_NO_RENEGOTIATION
      366 TLS1_ALERT_UNSUPPORTED_EXT

      Additional BR_ERR* error codes

      \ No newline at end of file diff --git a/TM163x/index.html b/TM163x/index.html new file mode 100644 index 0000000000..c01dee94bd --- /dev/null +++ b/TM163x/index.html @@ -0,0 +1,45 @@ + TM1637, TM1638 and MAX7219 Seven-Segment Display - Tasmota
      Skip to content

      TM1637, TM1638 and MAX7219 Seven-Segment Display~

      This feature is included only in tasmota*-display.bin precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_DISPLAY
      +#define USE_DISPLAY
      +#endif
      +
      +#ifndef USE_DISPLAY_TM1637
      +#define USE_DISPLAY_TM1637
      +#endif
      +
      In case you want MAX7219 also add:
      #ifndef USE_DISPLAY_MAX7219
      +#define USE_DISPLAY_MAX7219
      +#endif
      +
      In order to use TM1368 leds and buttons, see instructions in TM1638 leds and buttons

      TM1637 TM1638 MAX7219
      TM1637          TM1638        MAX7219

      The TM1637, TM1638 and MAX7219 display modules are small (~ 10mm high digits) 7-segment, LED-based display units with 4/6 digits (TM1637), 8 digits and 8 digits (TM1638 and MAX7219) respectively. They use a two-wire (TM1637) or three-wire (TM1638 and MAX7219) I2C-like (but not exactly) protocol for communication with MCUs like the ESP8266 / ESP32 / Arduino etc.,

      These modules are a great way to add a simple numeric display to any MCU project.

      Features~

      The Tasmota support for these modules can --

      • display Numbers and Floats with control over position and leading zeros.
      • display basic Text, for example, sending the text 22.5^ will display:
        22.5d
      • display Raw segments.
      • display Level, like a bar graph
      • display Scrolling text
      • display a Clock in 12 hr and 24 hr format
      • set Brightness (8 levels) and Clear the display.

      Configuration~

      Wiring~

      The TM1637 module needs to be connected to two GPIO pins and a 3.3V-5V power supply.

      TM1637 ESP based module
      CLK GPIOx
      DIO GPIOy
      VCC 3.3V (e.g., ESP-01) or 5V (e.g., Wemos D1 Mini)
      GND GND

      The TM1638 module needs to be connected to three GPIO pins and a 5V power supply.

      TM1638 ESP based module
      CLK GPIOx
      DIO GPIOy
      STB GPIOz
      VCC 5V
      GND GND

      The MAX7219 module needs to be connected to three GPIO pins and a 5V power supply.

      MAX7219 ESP based module
      CLK GPIOx
      DIN GPIOy
      CS GPIOz
      VCC 5V
      GND GND

      Tasmota Settings~

      In Tasmota's Configuration -> Configure Module page, assign:

      For TM1637

      1. GPIOx to TM1637 CLK
      2. GPIOy to TM1637 DIO

      For TM1638

      1. GPIOx to TM1638 CLK
      2. GPIOy to TM1638 DIO
      3. GPIOz to TM1638 STB

      For MAX7219

      1. GPIOx to MAX7219 CLK
      2. GPIOy to MAX7219 DIN
      3. GPIOz to MAX7219 CS

      For example, if x=0 and y=2, z=4 then the module configuration would look like the following:

      TM1637
      TM1637

      TM1638
      TM1638

      MAX7219
      MAX7219

      Initial Setup~

      The power toggle button in webUI turns the display on or off. However, if there are additional relays defined, resulting in multiple power toggle buttons in WebUI, display power will create and map to the last button. Thus, it is necessary to ensure that relays are numbered from 1, otherwise a conflict will occur with the display power.

      In the console, turn ON the display with the command Power 1.

      DisplayModel~

      Once the GPIO configuration is saved and the ESP8266 / ESP32 module restarts, set the Display Model to 15 using the command:

      DisplayModel 15

      DisplayWidth~

      If you have a TM1637 with 6 digits, set the number of digits using the command:

      DisplayWidth 6

      DisplayType~

      The 6-digit TM1637 has 2 known variants with different hardware wiring for the digit ordering.
      You can switch between these two variants with the following commands:

      DisplayType 0 - Use this for the Type 0 variant, with wiring similar to the TM1637 4-digit modules
      DisplayType 1 - Use this for the Type 1 variant, with a different wiring that causes the text 123456 to appear as 321654

      After the ESP8266/ESP32 module restarts again, the display module is ready to accept commands.

      DisplayModes~

      DisplayModes 0..3 are supported. The firmware default is DisplayMode 1.

      To use the Seven-Segment specific Display- commands described below, set the DisplayMode to 0 with:

      DisplayMode 0

      DisplayDimmer~

      The brightness of the display can be set using the DisplayDimmer command.

      DisplayDimmer 0..100 maps the brightness value to 8 steps from 0..7

      For a minimum brightness, make sure this value is at least about 13

      7-Segment specific Commands~

      The Display- commands listed below can be used from the Tasmota web-console, MQTT, and from Tasmota Rules

      TM1638 leds and buttons~

      In order to use the TM1638 leds and buttons, you must self-compile tasmota with the following in your user_config_override.h:

      #define USE_DISPLAY
      +#define USE_DISPLAY_TM1637
      +
      +#define USE_TM1638 // Add support for TM1638 switches copying Switch1 .. Switch8 (+1k code)
      +// #define TM1638_USE_AS_BUTTON // Add support for buttons
      +#define TM1638_USE_AS_SWITCH // Add support for switches (default)
      +#define TM1638_MAX_DISPLAYS 8 // Add support for power control 8 displays
      +#define TM1638_MAX_KEYS 8 // Add support for 8 keys
      +#define TM1638_MAX_LEDS 8 // Add support for 8 leds    
      +

      This will define the buttons as Switches. If you want to use them as Buttons, invert the comments on the lines:

      #define TM1638_USE_AS_BUTTON // Add support for buttons
      +//#define TM1638_USE_AS_SWITCH // Add support for switches (default)
      +

      The 8 leds will be created as Relays. If no other relays are defined, they will start as Relay1 up to Relay8. A 9th relay is assigned to the 7-segment display.

      The 8 buttons will be created by default as Switches with all the features of switches including debouncing and all Switchmodes.

      Alternatively, using #define TM1638_USE_AS_BUTTON they would be created as Buttons fully equivalent to a native Tasmota Button on a GPIO.

      In all cases, leds, switches or buttons index will be assigned after native relays, switches or buttons get assigned. Other drivers providing relays, buttons or switchs initialized before TM1368 will be also get index assigned before TM1638. So actual indexes may depends on what other GPIO/drivers are configured on your system.

      See rule sample below.

      Commands and Usage~

      Console/MQTT Commands Description values
      DisplayClear Clears the display
      DisplayNumber Clears and then displays number without decimal. command e.g., "DisplayNumber 1234".
      Control position, leading zeros, and length.
      position can be 0 (left-most) to NUM_DIGITS-1 (right-most),
      leading zeros can be 1 or 0 (default),
      length can be 1 to NUM_DIGITS (4 or 6).
      Command example: DisplayNumber 12, 1, 1, 3 This will display:
      012
      num [, position[, leading_zeros[, length]]]
      DisplayNumberNC Display integer number as above, but without clearing first. e.g., "DisplayNumberNC 1234". Usage is same as above. same as above
      DisplayFloat Clears and then displays float (with decimal point) command e.g., "DisplayFloat 12.34".
      Control position, precision, length and alignment.
      position can be 0 (left-most) to NUM_DIGITS-1 (right-most),
      precision can be 0 to NUM_DIGITS (default),
      length can be 1 to NUM_DIGITS (4 or 6).
      alignment can be 0 for left and 1 for right.
      Command example: DisplayFloat 2.48, 1, 1, 2 This will display:
      2.5
      num[, position[, precision[, length [, alignment]]]]
      DisplayFloatNC Displays float (with decimal point) as above, but without clearing first. command e.g., "DisplayFloatNC 12.34" same as above
      DisplayRaw Takes up to NUM_DIGITS comma-separated integers (0-255) and displays raw segments.
      Each number represents a 7-segment digit. Each 8-bit number represents individual segments of a digit.
      Segment a=1, b=2, c=4, d=8, e=16, f=32, g=64 and h (decimal point)=128.
      To turn on all segments, the number would be 1+2+4+8+16+32+64+128 = 255
      For example, the command DisplayRaw 0, 2, 255, 255 would display:
      8.8.
      position, length, num1 [, num2[, num3[, num4[, ...up to NUM_DIGITS numbers]]...]
      DisplayText Clears and then displays basic text. Command e.g., DisplayText a.b12
      Control length and position of the displayed text.
      length can be 1 to NUM_DIGITS ,
      position can be 0 (left-most) to NUM_DIGITS-1 (right-most)

      A caret(^) symbol in the text input is displayed as the degrees(°) symbol. This is useful for displaying Temperature (or angle)!
      For example, the command DisplayText 22.5^ will display:
      22.5d
      text[, position[, length]]
      DisplayTextNC Displays text without first clearing the display. Usage is same as above. same as above
      DisplayScrollText Displays scrolling text, up to 50 characters.
      If num_iterations is not specified, it scrolls indefinitely, until another Display- command is issued. Optionally, specifying num_iterations causes the scrolling to stop after the specified number of iterations.
      Command examples:
      DisplayScrollText tasmota is awesome -- causes indefinite scrolling
      DisplayScrollText tasmota is awesome, 5 -- causes scrolling to stop after 5 iterations
      text [, num_iterations]
      DisplayScrollDelay Sets the speed of text scroll. Smaller delay = faster scrolling. 0 to 15
      DisplayLevel Display a horizontal bar graph. Command e.g., DisplayLevel 50 will display:

      0 to 100
      DisplayClock Displays a clock.
      DisplayClock 1 displays a clock in 12-hour format.
      DisplayClock 2 displays a clock in 24-hour format.
      DisplayClock 0 turns off the clock and clears the display
      1 or 2 or 0

      Usage in Rules~

      All the above commands can be used in Tasmota Rules, as usual.

      For example, a simple digital thermometer can be implemented by connecting a DHT22 Temperature-Humidity Sensor and a TM1637 to a Wemos D1 Mini, and writing a Rule like the following:

      Rule1
      +ON Tele-AM2301#Temperature DO DisplayText %value%^ ENDON
      +
      Another example, using a MAX7219 and a SHT3X temp/humidity sensor, with value comparison so displaytext only fires when the value changes. The first four digits display the temperature and the second four digits display the humidity. The DisplayTextNC command is used to leave unused digits illuminated, so both numbers can be independently updated.
      Rule1
      +on sht3x#Temperature!=%var1% do backlog displaytextnc %value%^;var1 %value% endon on sht3x#Humidity!=%var2% do backlog displaytextnc %value%h,4;var2 %value% endon on system#init do power 1 endon
      +

      TM1638 in rules~

      As the leds and buttons are fully equivalent to Relays and Buttons/Switches, they can be used in rules in exactly the same way as native Relay/Power, Buttons/Switches.

      Here is a little ruleset which implement a 8-digit code entry (using only digit 1 to 8).

      At start Time is displayed (DisplayMode 1) but as soon as you type in the 1st key, it starts the entry mode. After each digit entry the user has up to 5 seconds to enter the next digit. It (s)he fails, the state machine is reset and the display reverts showing Time.

      Once all 8 digits are entered, the resulting string is stored in var3 as a text message.

      This is only a sample which can be adapted for example to send the code through MQTT.

      DisplayModel 15
      +DisplayWidth 8
      +
      +rule1 
      +  on system#init do event reset endon 
      +  on event#reset do backlog ruletimer1 0; displaymode 1; var1 1; var2 " endon
      +  on button1#state do event key=1 endon
      +  on button2#state do event key=2 endon 
      +  on button3#state do event key=3 endon 
      +  on button4#state do event key=4 endon 
      +  on button5#state do event key=5 endon 
      +  on button6#state do event key=6 endon 
      +  on button7#state do event key=7 endon 
      +  on button8#state do event key=8 endon 
      +  on event#key do backlog var2 %var2%%value%; displaymode 0; displaytext %var2%%value%; ruletimer1 5; add1 1 endon 
      +  on rules#timer=1 do event reset endon 
      +  on var1#state==9 do backlog ruletimer1 5; var3 The Code is %var2% endon
      +
      +rule1 1
      +

      TM1637 Images~

      TM1637
      The TM1637 4-digit module (front and back)




      TM1637-Wemos
      TM1637 4-digit module with Wemos D1 Mini

      \ No newline at end of file diff --git a/TM1650/index.html b/TM1650/index.html new file mode 100644 index 0000000000..cb96c85d60 --- /dev/null +++ b/TM1650/index.html @@ -0,0 +1,24 @@ + TM1650 Seven-Segment Display - Tasmota
      Skip to content

      TM1650 Seven-Segment Display~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_I2C
      +#define USE_I2C
      +#endif
      +
      +#ifndef USE_DISPLAY
      +#define USE_DISPLAY
      +#endif
      +#ifndef USE_DISPLAY_MODES1TO5
      +#define USE_DISPLAY_MODES1TO5
      +#endif
      +#ifndef USE_DISPLAY_TM1650
      +#define USE_DISPLAY_TM1650
      +#endif
      +
      +// ONLY XY-Clock
      +#ifndef USE_RTC_CHIPS 
      +#define USE_RTC_CHIPS
      +#endif
      +#ifndef USE_DS3231
      +#define USE_DS3231
      +#endif
      +

      TM1650 - XY-Clock TM1650 - 303WiFiLC01
      TM1650 - XY-Clock          TM1650 - 303WiFiLC01       

      The TM1650 display modules are usually found in "cheap" Wifi LED clock. They manage 7-segment LED displays (~ 27 mm high) with 4 digits. They use two wire I2C protocol to communicate with MCU such as ESP8266/ESP32/Arduino etc.,

      Features~

      The Tasmota support for these modules can --

      • display Numbers and Floats with control over position and leading zeros.
      • display basic Text, for example, sending the text 22.5^ will display:
        22.5d
      • display Raw segments.
      • display Level, like a bar graph
      • display Scrolling text
      • display a Clock in 12 hr and 24 hr format
      • set Brightness (8 levels) and Clear the display.

      Configuration~

      Wiring~

      The TM1650 module needs to be connected to I2C GPIO pins and a 3.3V-5V power supply.

      TM1650 ESP based module
      SCL GPIOx
      SDA GPIOy
      VCC 3.3V (e.g., ESP-01) or 5V (e.g., Wemos D1 Mini)
      GND GND

      Tasmota Settings~

      In Tasmota's Configuration -> Configure Module page, assign:

      For TM1650

      1. GPIOx to I2C SCL
      2. GPIOy to I2C SDA


      For example, with XY-Clock hardware, the module configuration would look like the following:

      TM1650 - XY-Clock
      TM1650

      Initial Setup~

      The power toggle button in webUI turns the display on or off. However, if there are additional relays defined, resulting in multiple power toggle buttons in WebUI, display power will create and map to the last button. Thus, it is necessary to ensure that relays are numbered from 1, otherwise a conflict will occur with the display power.

      In the console, turn ON the display with the command Power 1.

      DisplayModel~

      Once the GPIO configuration is saved and the ESP8266 / ESP32 module restarts, set the Display Model to 20 using the command:

      DisplayModel 20

      DisplayType~

      The 6-digit TM1637 has 2 known variants with different hardware wiring for the digit ordering.
      You can switch between these two variants with the following commands:

      DisplayType 0 - Use this for the Type 0 variant XY-Clock, with wiring is equal to datasheet
      DisplayType 2 - Use this for the Type 2 variant 303WifiLC01, with a different wiring

      After the ESP8266/ESP32 module restarts again, the display module is ready to accept commands.

      DisplayModes~

      DisplayModes 0..3 are supported. The firmware default is DisplayMode 1.

      To use the Seven-Segment specific Display- commands described below, set the DisplayMode to 0 with:

      DisplayMode 0

      DisplayDimmer~

      The brightness of the display can be set using the DisplayDimmer command.

      DisplayDimmer 0..100 maps the brightness value to 8 steps from 0..7

      For a minimum brightness, make sure this value is at least about 13

      7-Segment specific Commands~

      The Display- commands listed below can be used from the Tasmota web-console, MQTT, and from Tasmota Rules

      Commands and Usage~

      Console/MQTT Commands Description values
      DisplayClear Clears the display
      DisplayNumber Clears and then displays number without decimal. command e.g., "DisplayNumber 1234".
      Control position, leading zeros, and length.
      position can be 0 (left-most) to NUM_DIGITS-1 (right-most),
      leading zeros can be 1 or 0 (default),
      length can be 1 to NUM_DIGITS (4 or 6).
      Command example: DisplayNumber 12, 1, 1, 3 This will display:
      012
      num [, position[, leading_zeros[, length]]]
      DisplayNumberNC Display integer number as above, but without clearing first. e.g., "DisplayNumberNC 1234". Usage is same as above. same as above
      DisplayFloat Clears and then displays float (with decimal point) command e.g., "DisplayFloat 12.34".
      Control position, precision, and length.
      position can be 0 (left-most) to NUM_DIGITS-1 (right-most),
      precision can be 0 to NUM_DIGITS (default),
      length can be 1 to NUM_DIGITS (4 or 6).
      Command example: DisplayFloat 2.48, 1, 1, 2 This will display:
      2.5
      num[, position[, precision[, length]]]
      DisplayFloatNC Displays float (with decimal point) as above, but without clearing first. command e.g., "DisplayFloatNC 12.34" same as above
      DisplayRaw Takes up to NUM_DIGITS comma-separated integers (0-255) and displays raw segments.
      Each number represents a 7-segment digit. Each 8-bit number represents individual segments of a digit.
      Segment a=1, b=2, c=4, d=8, e=16, f=32, g=64 and h (decimal point)=128.
      To turn on all segments, the number would be 1+2+4+8+16+32+64+128 = 255
      For example, the command DisplayRaw 0, 2, 255, 255 would display:
      8.8.
      position, length, num1 [, num2[, num3[, num4[, ...up to NUM_DIGITS numbers]]...]
      DisplayText Clears and then displays basic text. Command e.g., DisplayText a.b12
      Control length and position of the displayed text.
      length can be 1 to NUM_DIGITS ,
      position can be 0 (left-most) to NUM_DIGITS-1 (right-most)

      A caret(^) symbol in the text input is displayed as the degrees(°) symbol. This is useful for displaying Temperature (or angle)!
      For example, the command DisplayText 22.5^ will display:
      22.5d
      text[, position[, length]]
      DisplayTextNC Displays text without first clearing the display. Usage is same as above. same as above
      DisplayScrollText Displays scrolling text, up to 50 characters.
      If num_iterations is not specified, it scrolls indefinitely, until another Display- command is issued. Optionally, specifying num_iterations causes the scrolling to stop after the specified number of iterations.
      Command examples:
      DisplayScrollText tasmota is awesome -- causes indefinite scrolling
      DisplayScrollText tasmota is awesome, 5 -- causes scrolling to stop after 5 iterations
      text [, num_iterations]
      DisplayScrollDelay Sets the speed of text scroll. Smaller delay = faster scrolling. 0 to 15
      DisplayLevel Display a horizontal bar graph. Command e.g., DisplayLevel 50 will display:

      0 to 100
      DisplayClock Displays a clock.
      DisplayClock 1 displays a clock in 12-hour format.
      DisplayClock 2 displays a clock in 24-hour format.
      DisplayClock 0 turns off the clock and clears the display
      1 or 2 or 0

      Usage in Rules~

      All the above commands can be used in Tasmota Rules, as usual.

      For example, a simple digital thermometer can be implemented by connecting a DHT22 Temperature-Humidity Sensor and a TM1650 to a Wemos D1 Mini, and writing a Rule like the following:

      Rule1
      +ON Tele-AM2301#Temperature DO DisplayText %value%^ ENDON
      +
      \ No newline at end of file diff --git a/TSL2561/index.html b/TSL2561/index.html new file mode 100644 index 0000000000..90b954fd89 --- /dev/null +++ b/TSL2561/index.html @@ -0,0 +1,5 @@ + TSL2561 light sensor - Tasmota
      Skip to content

      TSL2561 light sensor~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_TSL2561
      +#define USE_TSL2561         // [I2cDriver16] Enable TSL2561 sensor (I2C address 0x29, 0x39 or 0x49) using library Joba_Tsl2561 (+2k3 code)
      +#endif
      +

      The TSL2561 luminosity sensor is an advanced digital light I2C sensor, ideal for use in a wide range of light situations.

      Configuration~

      Wiring~

      TSL2561 ESP
      GND GND
      VCC 3.3V
      SCL GPIOy
      SDA GPIOx
      INT not used

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. GPIOx to I2C SDA
      2. GPIOy to I2C SCL

      After a reboot the driver will detect TSL2561 automatically and display Illuminance.

      image

      Sensor sends a tele/%topic%/SENSOR JSON response:

      {"Time":"2019-11-03T21:04:05","TSL2561":{"Illuminance":21.180}}
      +

      Breakout Boards~

      \ No newline at end of file diff --git a/TX2x/index.html b/TX2x/index.html new file mode 100644 index 0000000000..454730f912 --- /dev/null +++ b/TX2x/index.html @@ -0,0 +1,34 @@ + TX20/TX23/WS2300-15 anemometer - Tasmota
      Skip to content

      TX20/TX23/WS2300-15 anemometer~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:
      For TX20 sensor

      #ifndef USE_TX20_WIND_SENSOR
      +#define USE_TX20_WIND_SENSOR      // Add support for La Crosse TX20 anemometer (+2k6/0k8 code)
      +#endif
      +
      For TX23 sensor
      #ifndef USE_TX23_WIND_SENSOR
      +#define USE_TX23_WIND_SENSOR      // Add support for La Crosse TX20 anemometer (+2k6/0k8 code)
      +#endif
      +
      For WS2300-15 sensor
      #ifndef USE_WS2300_WIND_SENSOR
      +#define USE_WS2300_WIND_SENSOR    // Add support for Technoline WS2300-15 anemometer
      +#endif
      +

      This setting compiles Tasmota with sensor statistical values (needs the higher value size of code), which are useful for the wind sensor, since the measured values naturally change very quickly. The values generated in addition to the wind speed and wind direction are:

      • Wind speed min/max
      • Wind speed average (∅)
      • Wind direction average (∅)
      • Wind direction range (∠) and min/max

      The average (∅) values are continuously calculated values.
      The range (∠) and min/max values are held for the time TelePeriod and reset after MQTT SENSOR message has been output.

      If you do not want the statistical calculation having speed and direction value only, use

      #ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS
      +#define USE_TX2X_WIND_SENSOR_NOSTATISTICS
      +#endif
      +

      in your user_config_override.h. This will save approx. 1k8 bytes of code.


      The TX20 (retired) and TX23 sensor supplies the two measured values wind speed and wind direction.

      The TX23 sensor is still available under various brand names such as "La Crosse", "Technoline". Unfortunately, the TX20 (at least in Europe) is almost impossible to get. However, the two sensors differ only in the protocol used, but deliver the same measured values.

      Configuration~

      Wiring~

      TX20 / WS2300-15~
      Pin Color Description ESP
      1 Brown/Black TxD GPIOx
      2 Red 3.3V 3.3V
      3 Green DTR GND
      4 Yellow GND GND
      TX23~
      Pin Color Description ESP
      1 Brown/Black TxD GPIOx
      2 Red 3.3V 3.3V
      3 Green - -
      4 Yellow GND GND

      Tasmota Settings~

      In the Configuration -> Configure Module page assign: GPIOx to TX2x (104)

      The TX2 pin needs a pull-up resistor. The internal pull-up is activated for this so you can use any useful GPIO except GPIO15 and GPIO16 (GPIO15 is always pulled low, GPIO16 has a built-in pull-down resistor).

      To be on the save side you can add an additional external 10k pull-up to TX2x GPIOx.

      After a reboot the driver will detect TX20/TX23/WS2300 automatically and display the wind data:

      image

      Sensor sends a tele/%topic%/SENSOR JSON response:

      {
      +  "Time": "2020-03-03T00:00:00+00:00",
      +  "TX23": {
      +    "Speed": {
      +      "Act": 14.8,
      +      "Avg": 8.5,
      +      "Min": 12.2,
      +      "Max": 14.8
      +    },
      +    "Dir": {
      +      "Card": "WSW",
      +      "Deg": 247.5,
      +      "Avg": 266.1,
      +      "AvgCard": "W",
      +      "Min": 247.5,
      +      "Max": 247.5,
      +      "Range": 0
      +    }
      +  },
      +  "SpeedUnit": "km/h"
      +}
      +

      Commands~

      The wind speed unit is set to km/h as default. You can change the unit using the command
      SpeedUnit <x> where <x> = 1…6: - 1 = m/s (meter per second) - 2 = km/h (kilometer per hour) - 3 = kn (knots) - 4 = mph (miles per hour) - 5 = ft/s (foot per second) - 6 = yd/s (yard per second)

      The changed unit will appear in the webUI and via MQTT SENSOR messages.

      \ No newline at end of file diff --git a/TasUI/index.html b/TasUI/index.html new file mode 100644 index 0000000000..55799b36b8 --- /dev/null +++ b/TasUI/index.html @@ -0,0 +1,3 @@ + TasUI - Tasmota
      Skip to content

      TasUI

      Warning

      Development of TasUI has stopped!!!

      TasUI is a zero-install device management interface web application for all your Tasmota devices. It will discover your deployed devices and allow you to set up and configure every device from a single dashboard. This initial version includes multiple views (Control, Health, Firmware, Wi-Fi, & MQTT) to allow you to quickly assess the state of your devices. There is also a detailed view (e.g., SetOptions, Status, etc.) available.

      TasUI provides a syntax-aware command interface for every Tasmota command by category (e.g., Configuration, Timers, Sensors, Lights, etc.) as well as the "familiar" Console interface to enter commands directly. This is a beta version (i.e., we expect you to find some unexpected features). As these issues are fixed, it will not require you to reinstall any software to get these fixes. This also applies to new features as they are added to the app.

      Join us on Discord!

      Docker~

      For those who prefer to have everything installed locally, a Docker image for linux amd64, arm32v7, arm64v8 and i386 is available for download.

      docker pull iotreboot/tasui:latest
      +docker run -p [PORT]:80 iotreboot/tasui:latest
      +

      Home Assistant~

      The add-on can be installed on amd64, arm32v7, arm64v8 and i386 builds. Simply add the TasUI repository on your addons list: https://github.com/iotreboot/TasUI

      No configuration is required.

      \ No newline at end of file diff --git a/TasmoAdmin/index.html b/TasmoAdmin/index.html new file mode 100644 index 0000000000..58e5f60626 --- /dev/null +++ b/TasmoAdmin/index.html @@ -0,0 +1 @@ + TasmoAdmin - Tasmota
      Skip to content

      TasmoAdmin

      TasmoAdmin is an administrative Website for Devices flashed with Tasmota.
      You can find it here: TasmoAdmin GitHub. It supports running on Windows, Linux and as Docker container.

      Features~

      • Login protected
      • Multi Update Process
        • Select devices to update
        • Automatic Modus downloads latest firmware bin from Tasmota GitHub
      • Show device information
      • Mobile Responsive (Bootstrap4)
      • Config devices
      • SelfUpdate function for TasmoAdmin (disabled for Docker)
      • NightMode (Enable/Disable/Auto) in settings
      • AutoScan to find Tasmota Devices
      • Support for multiple sensors
      • chat (beta)

      Installation~

      Docker~

      TasmoAdmin is available as a Docker image on GitHub packages.

      This is a Linux Alpine based image with Nginx and PHP7 installed. It supports multiple architectures, amd64 (i.e. Synology DSM), arm (i.e. Raspberry PI3) and arm64 (i.e. Pine64). Check out the Guide for TasmoAdmin on Docker for setup instructions.

      This is the recommended way to get up and running.

      Linux~

      Running TasmoAdmin on a Linux/Unix hosts requires the following:

      • A Webserver
        • apache2 recommended
        • php7 recommended (works with php5 too)
        • php-curl php-zip Modules installed

      You need to install a web server with php-zip and php-curl modules installed. Also mod_rewrite must be enabled. I suggest to look in the Guide for Ubuntu Server 16.04 and try to adjust it to your server OS.

      Example Images~

      Login Page~

      Login

      Start Page~

      Startpage

      Devices Page~

      Devices

      Devices Add/Edit Page~

      Device Add/Edit

      Config General Page~

      Device Config_GENERAL

      Config Network Page~

      Device Config_Network

      Update Devices Page~

      Device Update 1 Device Update 2 Device Update 3

      Settings Page~

      Settings Settings

      Mobile~

      Navi_M Startpage_M Devices_M Config_General_M Config_Network_M

      \ No newline at end of file diff --git a/Tasmota-Application/index.html b/Tasmota-Application/index.html new file mode 100644 index 0000000000..b2518be3bf --- /dev/null +++ b/Tasmota-Application/index.html @@ -0,0 +1,8 @@ + Tasmota Application Files - Tasmota
      Skip to content

      Tasmota Application Files~

      Easily import any configuration or script using the .tapp file extension

      Any file with .tapp (Tasmota Application) extension will be considered as an uncompressed ZIP, and if it contains a sub-file autoexec.be it will be executed. In parallel tasmota.wd (working dir) contains the archive tapp prefix to easily load other assets from the same archive.

      Example

      heating.tapp contains: autoexec.be and html.json. The sub-file html.json can be opened with f = open(tasmota.wd + "html.json", "r")

      Berry allow imports from files inside a Tasmota App (.tapp file). Also enabled sys module.

      Here is the code you should add in your autoexec.be inside tapp file:

      import sys
      +var wd = tasmota.wd
      +if size(wd) sys.path().push(wd) end
      +
      +# [...] you can import files from within the archive
      +
      +if size(wd) sys.path().pop() end
      +

      TAPP's~

      LCD/OLED Anti Screen Burn~

      • copy Antiburn.tapp to file system
      • Either issue Tasmota command Antiburn or programmatically using lv.antiburn() in Berry

      The LVGL screen will change from black to red to green to blue to white each second for 30 seconds. The anti-burn cleaning can be cancelled by touching the screen or it will complete after 30 seconds. Once complete the previous screen will be reloaded.

      Partition Management~

      Partition Wizard

      Partition Manager

      To run either of these apps, simply upload the .tapp file to the filesystem and reboot the board. After doing so, the app will appear on the consoles page in the GUI.

      Partition_Wizard

      Display Calibration~

      Tasmota Application useful for Touch Screen calibration (resistive touchscreens only). This application guides you through simple steps and generates automatically the required settings in display.ini (the ':M' line).

      1. First download DisplayCalibrate.tapp application and upload it in the file system, and restart.

      2. Make sure you are in orientation DisplayRotate 0

      3. In the console, type the command DisplayCalibrate

      You will see the following screens. Click on all 4 crosses near corners.

      ts_0

      ts_1_0

      ts_1_1

      ts_1_2

      ts_1_3

      Note: measures are taken every 50 ms and are averaged, and requires at least 3 measures (150ms).

      If everything went well, you will see the following screen. After reboot, your touchscreen is ready and calibrate.

      ts_ok

      If the geometry is wrong, you will see the following screen and no change is done to display.ini

      ts_nok

      \ No newline at end of file diff --git a/Tasmota-Device-Locator/index.html b/Tasmota-Device-Locator/index.html new file mode 100644 index 0000000000..292364ba94 --- /dev/null +++ b/Tasmota-Device-Locator/index.html @@ -0,0 +1 @@ + Tasmota Device Locator - Tasmota
      Skip to content

      Tasmota Device Locator~

      Locate Tasmota Devices on your network when you only know the subnet of the device. The search is done with JavaScript in a browser and no software installation is required. Only newer devices with HTTP Cross-Origin Resource Sharing (CORS) support will be found. CORS is disabled in Tasmota by default. Use the CORS command to change this setting.

      Tasmota Device Locator

      Usage~

      Enter any IP address in the subnet and it will search for devices on the entire subnet. For example, specify 192.168.0.0 to locate all devices in the range 192.168.0.0 to 192.168.0.255.

      If the devices are password protected also enter the password. The password will be tried on all devices. If they have different passwords, multiple searches must be performed.

      Run the live version~

      The live version can be found here

      Local Deployment~

      If you want to run your own server, the Tasmota Device Locator can be served by any static http server (not https). - Checkout the branch gh-pages. - Copy the files to the root of the web server

      Development~

      Sources can be found on github, Tasmota Device Locator.

      \ No newline at end of file diff --git a/Tasmota-Device-Manager/index.html b/Tasmota-Device-Manager/index.html new file mode 100644 index 0000000000..ae6bdf52ce --- /dev/null +++ b/Tasmota-Device-Manager/index.html @@ -0,0 +1 @@ + Tasmota Device Manager - Tasmota
      Skip to content

      Tasmota Device Manager or TDM is a GUI application written in Python for discovery and monitoring of Tasmota flashed devices.

      Screenshot

      Features~

      • clean, readable interface
      • autodetection of devices following the default topic template for Tasmota (%prefix%/%topic%/) and for HomeAssistant Auto Discovery protocol (%topic%/%prefix%/)
      • module and GPIO configuration
      • rules editor
      • devices with different syntax can be added manually
      • clean retained MQTT topic messages
      • toggleable active querying of telemetry
      • passive monitoring of state and telemetry (currently supported sensors are listed in "status8.json")
      • relay control via context menu on device list (all ON/OFF, or individual)
      • MQTT console with payload preview (dbl-click an entry to display), sorting and filtering
      • selectable detail columns in device list
      • BSSID aliasing for larger deployments

      Installation~

      Python 3.6+ is required. Clone the repo or download zip and extract, install prerequisites and run tdm.py using Python binary.

      Prerequisites~

      PyQt5:

      pip install PyQt5

      paho-mqtt:

      pip install paho-mqtt

      \ No newline at end of file diff --git a/Tasmota-IR/index.html b/Tasmota-IR/index.html new file mode 100644 index 0000000000..2ad8ea5386 --- /dev/null +++ b/Tasmota-IR/index.html @@ -0,0 +1,4 @@ + IR Communication - Tasmota
      Skip to content

      IR Communication

      The default Tasmota firmware variants include support for IR send/receive for a limited set of protocols (see IR Remote commands).

      Tasmota uses the IRremoteESP8266 library that supports numerous protocols. Each protocol consumes some memory, especially air conditioner protocols (up to 81k of flash size). Also, every protocol included increases the time to decode the IR signal.

      There are two additional Tasmota firmware variants that provide almost all IRremoteESP8266 protocols. This requires disabling some other features to keep code size manageable.

      • tasmota-ir is pre-packaged for IR bridge devices such as Automate Things IRBR, Eachen or YTF. Choose tasmota-ir if you are using an IR blaster.
      • tasmota-ircustom is used if you want to customize your features (additional sensors, language, etc.). See compile your own firmware.

      You can flash the binary or upgrade your existing one.

      To test that you have the correct firmware on your device issue the following command in the web UI Console:

      IRHVAC {"Vendor":"xx"}

      The output should be a list of the supported protocols/vendors. For example:

      RESULT = {"IRHVAC":"Wrong Vendor (COOLIX|DAIKIN|KELVINATOR|MITSUBISHI_AC|GREE|ARGO|TROTEC|TOSHIBA_AC|FUJITSU_AC|MIDEA|HAIER_AC|HITACHI_AC|HAIER_AC_YRW02|WHIRLPOOL_AC|SAMSUNG_AC|ELECTRA_AC|PANASONIC_AC|DAIKIN2|VESTEL_AC|TECO|TCL112AC|MITSUBISHI_HEAVY_88|MITSUBISHI_HEAVY_152|DAIKIN216|SHARP_AC|GOODWEATHER|DAIKIN160|NEOCLIMA|DAIKIN176|DAIKIN128|AMCOR)"}
      +

      See Codes for IR Remotes.

      Sending IR Commands~

      Send an IR remote control code as a decimal or hexadecimal string in a JSON payload. In order to send IR data, you must configure one of the free device GPIO as IRSend (8). Neither GPIO01 nor GPIO03 can be used.

      Command Parameters
      IRSend<x> <x> [optional] = number of times the IR message is sent. If not specified or 0..1, the message is sent only once (i.e., not repeated) (default)
      >1 = emulate a long-press on the remote control, sending the message <x> times, or sending a repeat message for specific protocols (like NEC)

      {"Protocol":"<value>","Bits":<value>,"Data":<value>,"DataLSB":<value>,"Repeat":<value>}

      "Protocol" or "Vendor" (select one of the following):
      RC5, RC6, NEC, SONY, PANASONIC, JVC, SAMSUNG, WHYNTER, AIWA_RC_T501, LG, MITSUBISHI, DISH, SHARP, DENON, SHERWOOD, RCMM, SANYO_LC7461, RC5X, NEC (non-strict), NIKAI, MAGIQUEST, LASERTAG, CARRIER_AC, MITSUBISHI2, HITACHI_AC1, HITACHI_AC2, GICABLE, LUTRON, PIONEER, LG2, SAMSUNG36, LEGOPF, INAX, DAIKIN152

      "Bits":1..64 = required number of data bits
          for PANASONIC protocol this parameter is the the address, not the number of bits

      "Data":0x1..0xFFFFFFFFFFFFFFFF = data frame as 64 bit hexadecimal.
          e.g., IRSend {"Protocol":"NEC","Bits":32,"Data":0x8166817E}
      Or
      "DataLSB":0x1..0xFFFFFFFFFFFFFFFF = data frame as 64 bit hexadecimal with LSB (each byte with bits reversed).
          e.g., IRSend {"Protocol":"NEC","Bits":32,"Data":0x8166817E}
      DataLSB comes handy with LSB-first (Least Significant Bit First) protocols like NEC, and makes decoding/encoding easier.

      "Repeat":0..<x> if 0 send the frame once, if >0 simulates a long press; Note: "Repeat":1 sends the message twice.

      "Channel":x [optional] to specifiy which channel to use. You can use up to 16 channels and they are defined when you define the GPIO.

      Alternatively, you can send IR remote control codes using RAW command encoding.
      See also SetOption29 - Set IR received data format
      SetOption38 - Set IR received protocol sensitivity
      SetOption58 - IR Raw data in JSON payload

      Sending IRHVAC Commands~

      Command Parameters
      IRHVAC Send HVAC IR remote control code as JSON payload

      IRHVAC {"Vendor":"Mitsubishi_Heavy_152", "Power":"On","Mode":"Hot","FanSpeed":3,"Temp":22.5}

      "Protocol" or "Vendor" (select one of the following):
      COOLIX, DAIKIN, KELVINATOR, MITSUBISHI_AC, GREE, ARGO, TROTEC, TOSHIBA_AC, FUJITSU_AC, MIDEA, HAIER_AC, HITACHI_AC, HAIER_AC_YRW02, WHIRLPOOL_AC, SAMSUNG_AC, ELECTRA_AC, PANASONIC_AC, DAIKIN2, VESTEL_AC, TECO, TCL112AC, MITSUBISHI_HEAVY_88, MITSUBISHI_HEAVY_152, DAIKIN216, SHARP_AC, GOODWEATHER, DAIKIN160, NEOCLIMA, DAIKIN176, DAIKIN128

      "Model": Some HVAC have variants in protocols, this field allows to specify the variant, see detailed list.
      • Fujitsu_AC: ARRAH2E|ARDB1
      • Panasonic_AC: LKE|NKE|DKE|JKE|CKP|RKR
      • Whirlpool_AC: DG11J13A|DG11J104|DG11J1-04|DG11J191
      "Power":
      • On, Yes, True, 1
      • Off, No, False, 0
      "Mode":
      • Off, Stop
      • Auto, Automatic
      • Cool, Cooling
      • Heat, Heating
      • Dry, Drying, Dehumidify
      • Fan, Fanonly, Fan_Only
      "FanSpeed":
      • Auto, Automatic
      • Min, Minimum, Lowest, 1
      • Low, 2
      • Med, Medium, Mid, 3
      • High, Hi, 4
      • Max, Maximum, Highest, 5
      "SwingV": vertical swing of Fan
      • Auto, Automatic, On, Swing
      • Off, Stop
      • Min, Minimum, Lowest, Bottom, Down
      • Low
      • Mid, Middle, Med, Medium, Centre, Center
      • High, Hi
      • Highest, Max, Maximum, Top, Up
      "SwingH": horizontal swing of Fan
      • Auto, Automatic, On, Swing
      • Off, Stop
      • LeftMax, Left Max, MaxLeft, Max Left, FarLeft, Far Left
      • Left
      • Mid, Middle, Med, Medium, Centre, Center
      • Right
      • RightMax, Right Max, MaxRight, Max Right, FarRight, Far Right
      • Wide
      "Celsius": temperature is in Celsius ("On") or Fahrenheit ("Off")
      "Temp": Temperature, can be float if supported by protocol
      "Quiet": Quiet mode ("On" / "Off")
      "Turbo": Turbo mode ("On" / "Off")
      "Econo": Econo mode ("On" / "Off")
      "Light": Light ("On" / "Off")
      "Filter": Filter active ("On" / "Off")
      "Clean": Clean mode ("On" / "Off")
      "Beep": Beep active ("On" / "Off")
      "Sleep": Timer in seconds
      "StateMode":
      • SendOnly (default)
      • StoreOnly
      • SendStore

      Controlling ACs with toggle fields~

      Some ACs such as Airwell, Whirlpool and others use a differential IR protocol for some properties. If "power" is a toggle property, then value of '1' in the protocol will mean "turn off" if the AC is currently "on" and it will mean "turn on" if the AC is currently "off". This could also be the case for other properties such as "light", "swing", etc. Since Tasmota and IRRemote8266 send and receive absolute states (i.e. the HVAC JSON object has a field called 'Power' not 'PowerToggle'), some functionality was added to keep track of the predicted state of the AC. This way, if the predicted state is in-sync with the actual state, the device can transmit a correct IR packet to transition the AC from the previous state to the desired state. This logic is controlled by the StateMode HVAC property. If your AC IR protocol sends absolute values you can ignore this property. If your AC has a differential protocol Tasmota will be able to control the AC assuming the Tasmota IR device has at most one differential AC in IR range. If you would like to also control your AC with the physical remote your Tamsota IR device will need to have an IR receiver so that it can sync with the actual state.

      If at some point the state in Tasmota and the actual state get out of sync, you can specify StateMode=StoreOnly to update Tasmota with the actual state. This will update state but not send an IR command.

      Normally when receiving an IR command via the IR receiver the command will be processed and the state will be updated. As a result it is not needed to store state when sending as it will cause duplication and the toggle will happen twice. The most common operation mode, and default, therefore is StateMode=SendOnly.

      If your Tasmota device does not have an IR receiver you can still control a differential AC with it by specifying StateMode=SendStore but you will not be able to use a physical remote without loosing sync between the actual and predicted states.

      Receiving IR Commands~

      If you have an IR receiver, a message will be logged each time an IR message is seen. IR driver will try to decode the message against all supported protocols. If unrecognized, the "Protocol":"UNKNOWN" will be shown. In this case, the "Data" field contains a hash of the received message. The hash can't be used to send the a message, but the same hash will be produced by the same message.

      An inexpensive IR sensor such as a TSOP1838 can be connected to a device running Tasmota. Configure a free device GPIO as 'IRrecv (51)'. When Tasmota receives an IR message, the data portion of the payload has the same format as the IRsend parameter.

      {"IrReceived":{"Protocol":"<value>","Bits":<value>,"Data":<value>}}
      +

      This JSON payload data can be used in a rule such as:

      ON IrReceived#Data=<value> DO <command> ENDON
      +

      If the data is received on an unknown protocol use SetOption58 1. See here.

      Examples:
      Pioneer Vol+
      MQT: tele/tasmota/IR1/RESULT = {"IrReceived":{"Protocol":"PIONEER","Bits":64,"Data":"0xA55A50AFA55A50AF","DataLSB":"0xA55A0AF5A55A0AF5","Repeat":0}}

      Pioneer Vol-
      tele/tasmota/IR1/RESULT = {"IrReceived":{"Protocol":"PIONEER","Bits":64,"Data":"0xA55AD02FA55AD02F","DataLSB":"0xA55A0BF4A55A0BF4","Repeat":0}}

      Toshiba (NEC): Channel 1
      MQT: tele/tasmota/IR1/RESULT = {"IrReceived":{"Protocol":"NEC","Bits":32,"Data":"0x02FD807F","DataLSB":"0x40BF01FE","Repeat":0}}

      Toshiba (NEC): Channel 2
      MQT: tele/tasmota/IR1/RESULT = {"IrReceived":{"Protocol":"NEC","Bits":32,"Data":"0x02FD40BF","DataLSB":"0x40BF02FD","Repeat":0}}

      Toshiba (NEC): Channel 3
      MQT: tele/tasmota/IR1/RESULT = {"IrReceived":{"Protocol":"NEC","Bits":32,"Data":"0x02FDC03F","DataLSB":"0x40BF03FC","Repeat":0}}

      As you can see above, "DataLSB" are easier to decode than "Data". The third byte contains the command, and the fourth byte is the third with all bits reversed.

      Example of HVAC message:
      MQT: tele/tasmota/IR1/RESULT = {"IrReceived":{"Protocol":"MITSUBISHI_HEAVY_152","Bits":152,"Data":"0xAD513CE51A08F705FA02FDC03F08F700FF807F","Repeat":0,"IRHVAC":{"Vendor":"MITSUBISHI_HEAVY_152","Model":-1,"Power":"on","Mode":"auto","Celsius":"on","Temp":22,"FanSpeed":"medium","SwingV":"off","SwingH":"off","Quiet":"off","Turbo":"off","Econo":"off","Light":"off","Filter":"off","Clean":"off","Beep":"off","Sleep":-1}}}

      MQT: tele/tasmota/IR1/RESULT = {"IrReceived":{"Protocol":"COOLIX","Bits":24,"Data":"0xB25F78","DataLSB":"0x4DFA1E","Repeat":0,"IRHVAC":{"Vendor":"COOLIX","Model":-1,"Power":"on","Mode":"auto","Celsius":"on","Temp":22,"FanSpeed":"medium","SwingV":"off","SwingH":"off","Quiet":"off","Turbo":"off","Econo":"off","Light":"on","Filter":"off","Clean":"on","Beep":"off","Sleep":-1}}}

      RSL: RESULT = {"Time":"2019-09-09T21:52:35","IrReceived":{"Protocol":"PANASONIC_AC","Bits":216,"Data":"0x0220E004000000060220E00400032C805F06000EE0000081000089","Repeat":0,"IRHVAC":{"Vendor":"PANASONIC_AC","Model":2,"Power":"on","Mode":"auto","Celsius":"on","Temp":22,"FanSpeed":"medium","SwingV":"auto","SwingH":"middle","Quiet":"off","Turbo":"off","Econo":"off","Light":"off","Filter":"off","Clean":"off","Beep":"off","Sleep":-1}}}

      \ No newline at end of file diff --git a/TasmotaClient/index.html b/TasmotaClient/index.html new file mode 100644 index 0000000000..e4b0431496 --- /dev/null +++ b/TasmotaClient/index.html @@ -0,0 +1,12 @@ + Arduino MCU - Tasmota
      Skip to content

      Arduino MCU

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_TASMOTA_CLIENT
      +#define USE_TASMOTA_CLIENT    // Enable the driver
      +#endif
      +
      +#ifndef USE_TASMOTA_CLIENT_FLASH_SPEED
      +#define USE_TASMOTA_CLIENT_FLASH_SPEED 57600    // Configure the baud rate of the bootloader
      +#endif
      +
      +#ifndef USE_TASMOTA_CLIENT_SERIAL_SPEED 57600  
      +#define USE_TASMOTA_CLIENT_SERIAL_SPEED 57600    // Configure the baud rate at which the client microcontroller will be interfacing to Tasmota
      +#endif
      +

      It is possible to amend your existing Arduino Uno/Mini/Nano project to interface with a Tasmota powered ESP8266/ESP8285 generic development boards such as the Wemos D1 or NodeMCU branded hardware boards.

      The creation of a client driver interface implemented since Tasmota 7.0.0.2 enables this possibility.

      Please note that the USE_TASMOTA_CLIENT_FLASH_SPEED will depend on the variant of Arduino Uno/Mini/Nano board you are using - The general observation is that the 3.3V devices usually run at 57600 whereas the 5V devices usually run on 115200 but this is provided for guidance only as it has been found that some boards will not necessarily adhere to this. The main driving factor behind the baud rate is the crystal oscillator on the board which is usually 8Mhz for 3.3V variants and 16Mhz for 5V variants - hence 57600 being 1/2 of 115200.

      It should also be noted that this option is only really applicable if you're actually using an Arduino UNO/Mini/Nano as a client device and you want to be able to update the firmware on the Arduino OTA via the Tasmota Web UI.

      If you are using another type of microcontroller, obviously the OTA update functionality will not work (for now, until support is added for other microcontrollers). But if you are able to program the device manually there is no reason why you cannot use any microcontroller or development board as a client to your Tasmota powered ESP8266/8285 hardware.

      Once you have compiled your own variant with the correct settings and flashed the self-compiled binary to your Tasmota device it is time to make the necessary configuration within Tasmota.

      Configuring Tasmota to use the TasmotaClient functionality~

      The communication interface between Tasmota and your client micro-controller will be over serial communication. To make this possible you will need to configure two of the GPIO pins to Client TX and Client RX respectively. In addition to this you also need to define the GPIO which will be used to pull the reset pin of your client microcontroller down to GND or up to 3.3V, so either Client RST for normal reset behaviour (active low) or Client RSTi for inverted reset behaviour (active high)

      TasmotaClient GPIO Configuration

      As mentioned above it is possible to connect any microcontroller of your choice but for the purpose of this article, only the implementation of an Arduino Pro Mini (3.3V) will be covered.

      Getting things wired up~

      With Tasmota GPIO configuration provided above you may now proceed to make the necessary electrical connections between the ESP8266/ESP8285 and your client device, for example:

      ESP Arduino Pro Mini (3.3V)
      VCC VCC
      GND GND
      D2 (GPIO4) RX (0)
      D1 (GPIO5) TX (1)
      D4 (GPIO2) Reset (RST)

      So to visualize the above:

      Arduino Pro Mini

      Please verify your specific board's pin naming as they are not always exactly the same depending on where they came from!

      Compiling a test sketch for the client~

      The TasmotaClient driver requires your client to operate within specific parameters allowed by the driver itself so head over to the TasmotaClient Library and install the library in your local Arduino development environment.

      Once installed you should be able to access the examples from the menu system:

      Arduino Library Examples

      Make sure you have the correct board and speed selected:

      Arduino Board Configuration

      Now that everything is set, it's time to export the compiled binary by selecting it from the Arduino menu:

      Export compiled Binary

      Once completed head over to the known folder you chose in previous steps and locate the .hex file which should be in the same folder as where your sketch was originally saved to.

      You will most likely see a file listing like this:

      Arduino Sketch Folder

      You are interested in Blink.ino.eightanaloginputs.hex and can ignore the one which has bootloader as part of the file name since the Arduino Pro Mini already has the bootloader flashed. Take note of the location and name of this file as you will need it in the next step to upload the compiled file to your client device.

      Uploading a new hex file to your client device~

      If you are using an Arduino Pro Mini as is the case in this example you would have created a .hex file in the previous step. This file can be flashed directly to the Arduino Pro Mini via the Tasmota Web UI.

      Navigate to the Firmware Upgrade page where you would normally upload a new binary file to upgrade Tasmota. The same method is used to upload a HEX file to the client device. Tasmota will automatically decide where it will flash the upload based on whether you're uploading a .bin file or a .hex file.

      Firmware Upgrade Page

      Tasmota will prompt you for the firmware file you wish to upload - You need to navigate to the previously known location folder you chose in earlier steps and select the .hex file previously identified:

      File Selection

      After selecting the hex file you may proceed to click the Start Upgrade button:

      Start Upgrade Button

      The hex file will upload to Tasmota and Tasmota will flash the new client firmware onto the Arduino Pro Mini and present you with the following web page:

      Upgrade Success

      After the device completes a restart you should now have an Arduino Pro Mini running as a client where the blink is controlled from Tasmota's internal one-second callback.

      \ No newline at end of file diff --git a/Teleinfo/index.html b/Teleinfo/index.html new file mode 100644 index 0000000000..979e0fc8b9 --- /dev/null +++ b/Teleinfo/index.html @@ -0,0 +1,47 @@ + Teleinfo - Tasmota
      Skip to content

      Teleinfo

      This feature is not included in precompiled binaries.

      See below how to build you own Tasmota with Teleinfo enabled.

      Overview~

      Teleinfo is a Tasmota ENERGY driver for energy meters installed by French national electricity grid manager Enedis in all households. Teleinfo driver works with either legacy meters or the new Linky meters. It is based on Charles Hallard's LibTeleinfo.

      Teleinfo driver features:

      • Support for Legacy mode (mode Historique at 1200 baud) or Standard mode (9600 baud)
      • Extract fields to feed Tasmota standard ENERGY module allowing Tasmota to report standard SENSOR.ENERGY messages and all related features (PowerDelta, margins, ...)
      • Publish raw Teleinfo frame to MQTT for processing by the backend

      Support in french is available on Charles Hallard forums.

      Compiling with support for Teleinfo~

      As the feature is not included by default, you must compile your build.

      To enable Teleinfo, add the following line in your user_config_override.h and compile tasmota or tasmota32.

      #define USE_TELEINFO
      +

      But as I'm testing lot of boards, my preference goes to add my custom boards section into the file platformio_tasmota_cenv.ini

      For example on ESP8266 board with teleinfo and I2C display to display Energy data

      [env:tasmota-tic-display]
      +extends                     = env:tasmota
      +platform_packages           = ${common.platform_packages}
      +build_unflags               = ${common.build_unflags}
      +build_flags                 = ${common.build_flags}
      +                             -DUSE_TELEINFO
      +                             -DUSE_DISPLAY
      +

      Configure GPIOs for Teleinfo~

      Serial reception~

      Once you have flashed Tasmota with the support for Teleinfo, you need to configure the proper GPIO to receive the Teleinfo serial data with "TInfo RX"

      On ESP8266, suitable pins are

      • Standard UART0 RX pin on GPIO3 RX on GPIO3
      • Alternate UART0 RX pin on GPIO13 (D7) RX on D7

      On ESP32, most of the pins can be used since uart can be mapped on almost all pins.

      On Denky boards it's connected to GPIO33 and on new DenkyD4 it's on GPIO8.

      Optional enable pin~

      An optional Enable GPIO can be configured as "TInfo EN". Any GPIO output can be used for that purpose. Enable pin is for low power future use and avoid to have any consumption when there is nothing to do.

      For example schematic on custom Denky board (ESP32) is as follow

      Resistors values R3 and R4 need to be changed to work with standard and historique. New official values for R3 is 220ohm and R4 is 3.3Kohm. If you still have issues in Standard mode try to increase R3 up to 1Kohm

      As you can see GPIO33 is used for RX and GPIO4 for Enable (need set to 1 to be able to receive data)

      Setup in this case is

      • Enable pin on GPIO4 EN on GPIO4
      • RX pin on GPIO33 RX on GPIO33

      Another example on ESP8266 could be D5/GPIO14:

      EN on D5

      After selecting the GPIOs, click on "SAVE" and Tasmota will reboot.

      Configuring Teleinfo~

      You can customize Teleinfo with the command EnergyConfig <command> <parameter>. The list of supported commands and parameters are:

      Subcommand Parameters
      Without any command and parameter, displays the current active configuration.
      Historique Set Teleinfo in legacy (historique) mode at 1200 baud.
      Standard Set Teleinfo in standard mode at 9600 baud.
      NoRaw Disable sending of raw frame (see below).
      Full Enable sending of all frames in raw mode.
      Changed Enable sending raw frames only when data has changed.
      Skip n Skips n frames before sending raw frame.
      Limit Limit raw frames to values subject to fast change (Power, Current, ...)
      Stats Show Teleinfo frames errors statistics

      Changing mode from historique to standard and vice-versa will restart tamota.

      Check current configuration~

      First thing is to see how teleinfo is configured, for this you need to use EnergyConfig command from console

      17:24:56.758 CMD: EnergyConfig
      +17:24:56.766 TIC: Settings Mode:historique, RX:GPIO23, EN:None, Raw:noraw, Skip:0, Limit:0, Stats:0
      +17:24:56.783 MQT: emoncms/ch2i/factory/denky_D6C0C0/stat/RESULT = {"EnergyConfig":"Done"}
      +

      In this case we can see we are in historique mode, RX data is coming on GPIO32 (it's an ESP32) and Enable Pin is not used. No MQTT raw frame will be sent (noraw) on teleinfo frame reception, so keeping default config of telemetry. Statistics are disabled, so won't be displayed on WEB UI but still available on console.

      Energy from Teleinfo~

      If correct mode and GPIO configuration are set, you'll be able to see on main tasmota screen something like that

      It's happening only as soon as tasmota was able to read one valid frame from Teleinfo.

      Data received from teleinfo is used by tasmota in real time to display information but also to calculate daily energy consumption. Of course as Teleinfo is returning lot of data (such as kWh indexes) all interesting information is displayed on the Web interface. Since we know (because it's on teleinfo frame) the current contract we can display bargraph of charge (in %). This can help to see when main house smartmeter will shut down due to over consumption (for example)

      Default data~

      If configuration is left by default (and if you have configured MQTT of course) you will receive a frame each time telemtry happens (menu configuration -> configure logging -> telemetry period) which is by default every 5 minutes.

      You can check on console, the frame received is sent every 5 minutes under SENSOR topic as a JSON string. This frame as all default tasmota fields and also a object ENERGY related to tasmota Energy module.

      17:28:45.907 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"Time":"2021-10-26T17:28:45","ENERGY":{"TotalStartTime":"2021-10-25T15:22:04","Total":9098.245,"Yesterday":6822.685,"Today":2275.560,"Period": 9,"Power":170,"Current":1.000,"Load":6},"TIC":{"ADCO":"021528603314","OPTARIF":"HC..","ISOUSC":15,"HCHC":920750,"HCHP":2275560,"PTEC":"HP..","IINST":1,"IMAX":2,"PAPP":170,"HHPHC":"A","MOTDETAT":0},"ESP32":{"Temperature":36.1},"TempUnit":"C"}
      +17:33:45.906 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"Time":"2021-10-26T17:33:45","ENERGY":{"TotalStartTime":"2021-10-25T15:22:04","Total":9098.255,"Yesterday":6822.685,"Today":2275.570,"Period":10,"Power":190,"Current":1.000,"Load":6},"TIC":{"ADCO":"021528603314","OPTARIF":"HC..","ISOUSC":15,"HCHC":920750,"HCHP":2275570,"PTEC":"HP..","IINST":1,"IMAX":2,"PAPP":190,"HHPHC":"A","MOTDETAT":0},"ESP32":{"Temperature":36.1},"TempUnit":"C"}
      +17:38:45.905 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"Time":"2021-10-26T17:38:45","ENERGY":{"TotalStartTime":"2021-10-25T15:22:04","Total":9098.265,"Yesterday":6822.685,"Today":2275.579,"Period": 9,"Power":170,"Current":1.000,"Load":6},"TIC":{"ADCO":"021528603314","OPTARIF":"HC..","ISOUSC":15,"HCHC":920750,"HCHP":2275579,"PTEC":"HP..","IINST":1,"IMAX":2,"PAPP":170,"HHPHC":"A","MOTDETAT":0},"ESP32":{"Temperature":36.7},"TempUnit":"C"}
      +

      But in our case we have also a TIC object which contains all the frame received on teleinfo frame (last reveived value), for example in the frame above the TIC object is the following.

      {
      +  "TIC":{
      +    "ADCO":"021528603314",
      +    "OPTARIF":"HC..",
      +    "ISOUSC":15,
      +    "HCHC":920750,
      +    "HCHP":2275579,
      +    "PTEC":"HP..",
      +    "IINST":1,
      +    "IMAX":2,
      +    "PAPP":170,
      +    "HHPHC":"A",
      +    "MOTDETAT":0
      +  }
      +}
      +

      Sending data to other~

      Teleinfo is a very powerful tool to monitor house energy consumption that many of you may need to send these date to backend for other treatment. For example receiving power value every 5 minutes may be an issue to real time monitoring house consuption, this is why we introduced option to send data in real time.

      Best would be sending full received frame to MQTT for example, let's do that.

      Raw Frame going outside (MQTT)~

      To enable real time raw teleinfo frame to be sent to MQTT you need to use EnergyConfig Full command from console

      17:57:04.579 TIC: Raw to 'full'
      +17:57:04.594 MQT: emoncms/ch2i/factory/denky_D6C0C0/stat/RESULT = {"EnergyConfig":"Done"}
      +17:57:05.038 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"TIC":{"ADCO":"021528603314","OPTARIF":"HC..","ISOUSC":15,"HCHC":920750,"HCHP":2275613,"PTEC":"HP..","IINST":1,"IMAX":2,"PAPP":180,"HHPHC":"A","MOTDETAT":0}}
      +17:57:06.693 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"TIC":{"ADCO":"021528603314","OPTARIF":"HC..","ISOUSC":15,"HCHC":920750,"HCHP":2275613,"PTEC":"HP..","IINST":1,"IMAX":2,"PAPP":180,"HHPHC":"A","MOTDETAT":0}}
      +17:57:08.183 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"TIC":{"ADCO":"021528603314","OPTARIF":"HC..","ISOUSC":15,"HCHC":920750,"HCHP":2275614,"PTEC":"HP..","IINST":1,"IMAX":2,"PAPP":170,"HHPHC":"A","MOTDETAT":0}}
      +17:57:09.684 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"TIC":{"ADCO":"021528603314","OPTARIF":"HC..","ISOUSC":15,"HCHC":920750,"HCHP":2275614,"PTEC":"HP..","IINST":1,"IMAX":2,"PAPP":200,"HHPHC":"A","MOTDETAT":0}}
      +

      Then each frame received is sent immediately thru MQTT

      Skip frames~

      Depending on what you want to do on backend side it may be too much data and flood your database so we introduced Skip flag. For example we may need only one frame up to 10 to be send on backend (so free up some network traffic). To limit teleinfo frame to one each 10 frames, you need to use EnergyConfig Skip 10 command from console

      18:04:43.210 CMD: EnergyConfig Skip 10
      +18:04:43.214 TIC: Raw each 11 frame(s)
      +18:04:43.226 MQT: emoncms/ch2i/factory/denky_D6C0C0/stat/RESULT = {"EnergyConfig":"Done"}
      +18:05:00.438 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"TIC":{"ADCO":"021528603314","OPTARIF":"HC..","ISOUSC":15,"HCHC":920750,"HCHP":2275628,"PTEC":"HP..","IINST":1,"IMAX":2,"PAPP":170,"HHPHC":"A","MOTDETAT":0}}
      +18:05:17.438 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"TIC":{"ADCO":"021528603314","OPTARIF":"HC..","ISOUSC":15,"HCHC":920750,"HCHP":2275629,"PTEC":"HP..","IINST":1,"IMAX":2,"PAPP":170,"HHPHC":"A","MOTDETAT":0}}
      +

      As you can see we now send one full frame every approx 17s, choose your limit depending on frame length (of course) and serial speed. In historique mode a frame takes about 1.2s to be received.

      This works only if Raw mode has been selected with EnergyConfig Full.

      Relevant data~

      We can also going further, in previous mode, even if we limited the number of frames sent, a complete frame with all data was send. And this is not optimized because some fields in the frame may vary only once or twice a day PTEC or even never ADCO, ISOUSC, ... So we indroduced the Changed mode where only data changed from one frame to another is sent. To enable this mode, you need to use EnergyConfig Changed command from console

      18:28:57.610 CMD: EnergyConfig Changed
      +18:28:57.615 TIC: Raw to 'changed'
      +18:28:57.627 MQT: emoncms/ch2i/factory/denky_D6C0C0/stat/RESULT = {"EnergyConfig":"Done"}
      +18:29:00.425 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"TIC":{"PAPP":160}}
      +18:29:17.745 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"TIC":{"PAPP":190}}
      +18:29:34.689 MQT: emoncms/ch2i/factory/denky_D6C0C0/tele/SENSOR = {"TIC":{"PAPP":170}}
      +

      As you can see we now only data changed is sent.

      This works only if Raw mode has been selected with EnergyConfig Full and Skip mode should be disabled EnergyConfig Skip 0 for this to works properly.

      Don't worry about other fields, they will be send on each telemetry data as usual.

      Errors Statistics~

      You can check classic frame reception errors (checksum, size, format, ...), with EnergyConfig Stats x command. Setting to 1 allow to display statistics on WEB UI while setting to 0 disable this option, but you will be able to always see stats on console with command EnergyConfig Stats.

      You can clear Stats counters with EnergyConfig Stats 2

      Tasmota Rules examples~

      Raw Frame going outside (HTTP)~

      TBD, example to post HTTP data every 5 minutes

      Driving RGB Led depending on power~

      TBD, example to manage Led from Green (no charge) to Red (full charge)

      Tasmota Berry script examples~

      TBD, any idea is welcome

      Display info on display~

      TBD, any idea is welcome

      Raw frames examples~

      You can find various raw frames for standard and historique mode and different contracts on Teleinfo replay tool here

      Hardware information~

      Basic Schematics~

      Classic connectivity of teleinfo can be done with following schematic, works with historique and standard mode.

      Basic teleinfo Schematics

      DIY or ready made Shields~

      WeMos~

      Wemos Mini D1 (ESP8266) and MH-ET Live ESP32 Mini Kit

      Wemos Teleinfo shield

      You can get more information about this shield on github repository

      Raspberry Pi (Bare PCB)~

      You can also use or buy tiny Raspberry Pi shield to connect Teleinfo output signal to Tasmota device RX Pin

      PI Teleinfo shield

      Documentation and reference of this shield are available on tindie

      References~

      You can find a lot of information, such as schematics and wiring diagrams, on Charles' blog

      \ No newline at end of file diff --git a/Templates/index.html b/Templates/index.html new file mode 100644 index 0000000000..90bedca797 --- /dev/null +++ b/Templates/index.html @@ -0,0 +1,7 @@ + Templates - Tasmota
      Skip to content

      Templates

      Template is a definition of a device and how its GPIOs are assigned.

      Templates provide an easy way for you to create, modify and share configurations for various devices that have features supported in Tasmota.

      Bug

      Tasmota 9.1 completely redesigned template layout to allow for future expansion. Read more about the GPIO Conversion.

      To provide easy processing by Tasmota a template is written as a JSON string and looks like this:

      {"NAME":"Example Template","GPIO":[416,0,418,0,417,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45}
      +

      Tip

      Tasmota Supported Devices Repository has a complete list of supported devices. We encourage everyone who creates a template for a new unknown device to submit it using Add New Template form.

      How to Use~

      Go to Configuration - Configure Template ...

      How to get to template config

      ... and you'll end up looking at this screen.

      Configure Template

      Time to create your template.

      Creating Your Template~

      1. Change the template name (also defines the name for the module).
      2. Select a module to BASE your template on. If you're not sure, Module 18 is the best choice. In this example the device is based on Blitzwolf SHP (45) module.
      3. Configure the components assigned to the GPIOs to match your device. If you do not know what pins your device uses, read about the new device configuration procedure to determine the correct pin assignments. GPIO configuration

        • Any unused GPIO that has cannot have a peripheral connected should be set to None (0). In our example the device has no exposed GPIO's so the unused ones are set to 0 compared to the original BlitzWolf module.
        • GPIOs that can have peripherals connected to (exposed GPIOs) should be set to User (1). This allows future configuration through the Configure Module dialog without the need to create a new template.

        Example

        Sonoff TH has a jack connected to GPIO4 that allows a user to plug in a sensor. Assigning GPIO4 as User allows a Template to have correct GPIOs for this device even if nothing is plugged in. But, when a user decides to connect a sensor using the jack, GPIO4 can be set to the type of sensor through the Configure Module page.

      4. Click on Save and you'll see this message

      5. Finally, the device will reboot with the new template name

      Exporting Your Template~

      Now that you've set up your previously unsupported device in Tasmota it is time to share the knowledge:

      1. Check that Module 0 is selected in the Configuration - Configure Module menu.
      2. Open up Console and issue command Template which will output a string with the configuration of your currently active template. Our example gives the following:
      MQT: stat/tasmota/RESULT = {"NAME":"Merged","GPIO":[416,0,418,0,417,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45}
      +

      Copy the string {"NAME":"Merged","GPIO":[416,0,418,0,417,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45} and share it on the Tasmota Supported Devices Repository.

      Importing Templates~

      Go to Configuration - Configure Other

      How to get to template config

      When there:

      1. Paste the template string into the Template field
      2. Make sure you check Activate
      3. Click on Save.

      Template configuration

      The device will reboot with a name reflecting your template name and Module 0 selected which has your new template stored.

      If your device was previously configured you will have to manually change to Module 0 in *Configuration -> Configure Module*.

      It is finished

      Commands~

      A user provided template can be stored in Tasmota using the Template command. It has the following parameters.

      Parameter Description
      Show current Template
      0 Create template from active module
      1..71 Create template from a supported module
      { ... } Store template written in a JSON string
      255 Merge configured template and current module

      Template {"NAME":"Example Template","GPIO":[416,0,418,0,417,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45} stores a complete template based on the Generic module

      Template {"NAME":"AnotherModuleName"} updates the name of a stored template

      Template {"FLAG":0} changes FLAG value

      Template {"BASE":18} updates the base of a stored template to Generic

      Template {"CMND":"LedTable 1"} adds LedTable 1 command to the stored template

      After setting a template in command line it is necessary to issue Module 0 command if the device doesn't reboot on its own.

      Merge Template with Module~

      You can set up your device in module *Configuration -> Configure Module* and use command Template 255 to merge the settings of the Module with current template into a new Template named "Merged".

      Anatomy of a Template~

      Let's look again at our example template:

      {"NAME":"Example Template","GPIO":[416,0,418,0,417,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45}
      +

      The four properties with UPPERCASE property names have the following functionality:

      Property name Property value description
      NAME Up to 60 characters for the Module name
      GPIO Numbers from 0 to 65535 representing GPIO0 to GPIO5, GPIO09, GPIO10 and GPIO12 to GPIO16 and GPIO17 for A0 pin for ESP8266. ESP32 has more configurable GPIO's
      FLAG *Deprecated Replaced with GPIO17. 8 bit mask flag register.
      BASE Module number of a hard-coded device to be used when device specific functionality is needed
      CMND (Optional) Commands executed after activating the template

      GPIO~

      GPIO order~

      GPIO# |00| 01|02| 03|04| 05| 09| 10| 12| 13| 14| 15| 16| 17 
      +CODE  [416,0,418,0,417,2720,0,0,2624,32,2656,224,0,0] 
      +

      GPIO functionality~

      The GPIO functionality numbers are the same as shown by command GPIOs. In addition code 1 is added to select a GPIO as user configurable via the GUI Configure Module menu.

      Example

      In our example the GPIO 12 data element is 32 which corresponds to the Button1 component, according to the components table. If you change that template element to 160 it would then be assigned as a Switch1 component instead.

      Components~

      See Components for a complete list or use command Gpios 255 in console.

      FLAG~

      Before Tasmota 9.1 used to configure the ADC type. In new templates this should be set to 0 and the analog pin should be configured on GPIO17

      FLAG Feature description
      0 No features
      1 Analog value
      2 Temperature
      3 Light
      4 Button
      5 Buttoni
      15 User configured (same as User)

      BASE~

      BASE is the starting module setup for the custom template. Some modules include special programming. If your device is similar to an existing built-in module it is best to use that as a starting point. When you're not sure which BASE module is suitable for your device use the Generic (18) module. A list of hard-coded devices can be found in Modules.

      Example

      In the RGB Smart Plug template we used the BlitzWolf SHP (45) module as BASE since the power monitoring circuitry is identical but GPIO00 and GPIO02 were changed and an unused GPIO04 was added to enable the RGB LED function. Using that specific module we took advantage of that module's calibrated power monitoring special programming which the Generic (18) module does not use.

      CMND~

      CMND is an optional field in the template JSON string. It is used to embed commands for configuring the device instead of needing to type them in console. Multiple commands are separated with |.

      "CMND":"<any template related command>|<any template related command>|..."

      Commands will only be executed if the template is enabled (device module is set to 0), either before the template is loaded, when the activate box in the UI is selected or if the module 0 command is included in the CMND string itself.

      Disable LED gamma correction and remap RGB channels

      {"NAME":"Example","GPIO":[416,0,418,0,417,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45,"CMND":"LedTable 1|ChannelRemap 36"}
      +

      Total size of the template string should not exceed 500 chars.

      \ No newline at end of file diff --git a/Thermal-considerations/index.html b/Thermal-considerations/index.html new file mode 100644 index 0000000000..27608927ef --- /dev/null +++ b/Thermal-considerations/index.html @@ -0,0 +1 @@ + Thermal considerations - Tasmota
      Skip to content

      Thermal considerations

      Even though LEDs consume far less energy than incandescent bulbs, these devices contain more complex circuitries and sometimes they still produce more heat than they can handle. The produced heat is proportional to the LED intensities, and if it builds up faster than how it dissipates from the chassis, then the temperature will rise.

      The circuitries usually (but not necessarily) contain some last-resort countermeasure that shuts down the device when it is critically overheated, but it shouldn't be relied upon.

      When testing the device for the first time, especially at higher light intensities, monitor its temperature for some time, like for at least half an hour, and if it rises rapidly, then please consider:

      • Reducing the overall brightness
      • Using only one of the light sources (i.e. either the color LEDs, or the high-power white ones)

      For such a test use an electrically safe lamp in which you can access the bulb, and which you can easily disconnect from the mains anytime. For measuring the temperature the best tool is an IR thermometer: aim it at the heatsink part of the chassis from a direction perpendicular to it, from such a distance that its cone of sensitivity is fully on the chassis. If you choose to test the temperature by hand, then be prepared that it may be hot, so approach it slowly, and if you already feel its heat, then don't touch it.

      During this check please also consider that a bulb standing on your desk in a test socket has considerably better cooling than one in a closed armature right below your ceiling, so try to 'model' the operating conditions in which you plan to use the device.

      If you have found a solution that keeps the temperature stable, don't forget to configure a limit in your home automation system so you (or anyone else) won't accidentally set the device to overheating when it'll be already installed.

      Some of the devices' original firmwares do contain such software throttling, but as the thermal behaviour differs from one model to another, there is no generic way to apply the right amount of throttling that would be both required and enough for everyone, so "your mileage may vary".

      Technical background~

      Light bulb circuitries consist of 3 main stages:

      • A small power supply unit that converts the 230V or 110V mains to approx. 20V for the LEDs and 3.3V for the controller. This is a small switching-mode power supply, but usually of a parsimonius design, so it's usually barely adequate for the average power requirement, and sometimes not enough for the maximum.

      The main problem is not the transformer, but the voltage regulators: they produce heat proportionally to the current that's drawn through them, and they aren't connected to the heatsink, so all their heat goes just into the air within the bulb.

      • The controller module, usually a SoC that contains the CPU, memory, flash and wifi. It is a logic circuit, its heat production is negligible compared to the other stages.

      • The LED circuitry, meaning the LEDs themselves and their driver chips, usually on a separate board. They produce a lot of heat, but they are always connected to the chassis via either thermal grease or thermally conductive glue.

      So the problems are: - Voltage regulators produce heat proportional to light intensity - Their thermal coupling to the chassis is terrible: via a huge air gap - The chassis is not an effective heatsink (for aesthetic reasons it can't be) - The overall system is designed for the average conditions and not for the maximum.

      Thus our goal is to limit the power consumption to such a level that the heat produced can be dissipated by the chassis heatsink, and the way to achieve this is throttling down the light intensity and/or limiting the switched-on time.

      Measured values~

      SYF05 (Fcmila/Sunyesmart)~

      • At start: 23°C
      • After 10 minutes: 40°C (warm)
      • After 20 minutes: 48°C (hot)
      • After 30 minutes: 53°C (barely touchable)
      • After 40 minutes: 55°C

      At this point the thermal protection has shut the device down, and the local temperatures were: - Chassis: 55°C - RGB LED driver chip: 73°C - White LED driver chip: 76°C - Controller module: 76°C - Transformer: 85°C - Area around the voltage regulator: 91°C

      Sonoff B1~

      • At start: 26°C
      • After 10 minutes: 33°C
      • After 20 minutes: 40°C
      • After 30 minutes: 44°C
      • After 40 minutes: 47°C
      • After 50 minutes: 48°C
      • After 1 hour: 50°C

      The bulb was operational at this point, but I considered this temperature too high for sustained use.

      \ No newline at end of file diff --git a/Thermostat/index.html b/Thermostat/index.html new file mode 100644 index 0000000000..6f0cc5cbcf --- /dev/null +++ b/Thermostat/index.html @@ -0,0 +1,66 @@ + Thermostat - Tasmota
      Skip to content

      Thermostat

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #define USE_THERMOSTAT                           // Add support for Thermostat
      +    #define THERMOSTAT_CONTROLLER_OUTPUTS         1         // Number of outputs to be controlled independently
      +    #define THERMOSTAT_SENSOR_NAME                "DS18B20" // Name of the local sensor to be used
      +    #define THERMOSTAT_SENSOR_NUMBER              1         // Number of local sensors to be used
      +    #define THERMOSTAT_RELAY_NUMBER               1         // Default output relay number for the first controller (+i for following ones)
      +    #define THERMOSTAT_SWITCH_NUMBER              1         // Default input switch number for the first controller (+i for following ones)
      +    #define THERMOSTAT_TIME_ALLOW_RAMPUP          300       // Default time after last target update to allow ramp-up controller phase in minutes
      +    #define THERMOSTAT_TIME_RAMPUP_MAX            960       // Default time maximum ramp-up controller duration in minutes
      +    #define THERMOSTAT_TIME_RAMPUP_CYCLE          30        // Default time ramp-up cycle in minutes
      +    #define THERMOSTAT_TIME_SENS_LOST             30        // Maximum time w/o sensor update to set it as lost in minutes
      +    #define THERMOSTAT_TEMP_SENS_NUMBER           1         // Default temperature sensor number
      +    #define THERMOSTAT_TIME_MANUAL_TO_AUTO        60        // Default time without input switch active to change from manual to automatic in minutes
      +    #define THERMOSTAT_TIME_RESET                 12000     // Default reset time of the PI controller in seconds
      +    #define THERMOSTAT_TIME_PI_CYCLE              30        // Default cycle time for the thermostat controller in minutes
      +    #define THERMOSTAT_TIME_MAX_ACTION            20        // Default maximum thermostat time per cycle in minutes
      +    #define THERMOSTAT_TIME_MIN_ACTION            4         // Default minimum thermostat time per cycle in minutes
      +    #define THERMOSTAT_TIME_MIN_TURNOFF_ACTION    3         // Default minimum turnoff time in minutes, below it the thermostat will be held on
      +    #define THERMOSTAT_PROP_BAND                  4         // Default proportional band of the PI controller in degrees celsius
      +    #define THERMOSTAT_TEMP_RESET_ANTI_WINDUP     8         // Default range where reset antiwindup is disabled, in tenths of degrees celsius
      +    #define THERMOSTAT_TEMP_HYSTERESIS            1         // Default range hysteresis for temperature PI controller, in tenths of degrees celsius
      +    #define THERMOSTAT_TEMP_FROST_PROTECT         40        // Default minimum temperature for frost protection, in tenths of degrees celsius
      +    #define THERMOSTAT_TEMP_RAMPUP_DELTA_IN       4         // Default minimum delta temperature to target to get into rampup mode, in tenths of degrees celsius
      +    #define THERMOSTAT_TEMP_RAMPUP_DELTA_OUT      2         // Default minimum delta temperature to target to get out of the rampup mode, in tenths of degrees celsius
      +    #define THERMOSTAT_TEMP_PI_RAMPUP_ACC_E       200       // Default accumulated error when switching from ramp-up controller to PI in hundredths of degrees celsius
      +    #define THERMOSTAT_TIME_OUTPUT_DELAY          180       // Default output delay between state change and real actuation event (f.i. valve open/closed)
      +    #define THERMOSTAT_TEMP_INIT                  180       // Default init target temperature for the thermostat controller
      +    #define THERMOSTAT_TIME_MAX_OUTPUT_INCONSIST  3         // Default maximum time where the input and the output shall differ (for diagnostic) in minutes
      +    #define THERMOSTAT_TIME_MAX_AUTOTUNE          21600     // Maximum time for the PI autotune function to complete in seconds
      +    #define THERMOSTAT_DUTYCYCLE_AUTOTUNE         35        // Default duty cycle (in % over PI cycle time) for the step response of the autotune PI function
      +    #define THERMOSTAT_PEAKNUMBER_AUTOTUNE        8         // Default number of peak temperatures (max or min) to be used for the autotune PI function
      +    #define THERMOSTAT_TEMP_BAND_NO_PEAK_DET      1         // Default temperature band in thenths of degrees celsius within no peak will be detected
      +    #define THERMOSTAT_TIME_STD_DEV_PEAK_DET_OK   10        // Default standard deviation in minutes of the oscillation periods within the peak detection is successful    
      +

      Control over heating and cooling as a true HVAC unit

      Thermostat driver allows a Tasmota device, provided it receives the temperature input via MQTT or a locally connected sensor, to follow control heating/cooling strategies to reach the desired setpoint. The thermostat offers similar functions as feature rich commercial ones as the models found below:

      Pinout

      Typical setup: Heating floor system~

      A typical setup for heating room systems can be found in the picture below. A conventional room thermostat is connected to a heating floor valve actuator, both running at AC voltage (f.e. 220V). The thermostat is connected to neutral as well as to the phase, the actuator to the same neutral connection of the thermostat and to its actuation signal. The actuation signal will switch between the neutral voltage (actuation Off) and the phase voltage (actuation On).

      The conventional room thermostats offer nowadays either 2 point control with hysteresis or a more advanced PI (Proportional-Integral) control. The result of the PI control is typically transformed into a PWM signal with a pre-defined period and a variable duty cycle.

      Pinout

      Use of tasmota switch to bypass an existing wall thermostat~

      A tasmota switch can be installed in a way that it bypasses the existing wall thermostat. The advantage of this setup is that the thermostat driver offers the possibility to follow the output of the existing wall thermostat or acting autonomously. This setup allows a seamless integration with existing wall thermostats and gives the user the freedom to still use them.

      Below you can find an example of a Shelly switch bypassing a wall thermostat:

      Pinout

      Configuration for standalone application or bypass of existing wall thermostat~

      The driver by default does not consider the input switch states even if available and it's therefore suitable for standalone use. If the application requires to follow the command of the input once active (see bypass setup explained in previous section). This bypass function is specially useful to allow the user to use the device bypassed, in case of a wall thermostat, to allow this thermostat to be used if desired. To enable this bypass function, the following command is to be sent to the tasmota device:

      cmnd/Tasmota_Name/INPUTSWITCHUSE 1
      +

      Note

      Some devices (such as the Sonoff 4CH Pro R2) even if having input buttons to manually switch the state of the output, report always its inputs in active state no matter if the button is pressed or not. For these devices the parameter above needs to be set to 0, otherwise the thermostat driver will activate the output continuously and stay permanently in manual mode.

      Once active, the thermostat, in case of its input being active, will switch to manual mode and set as output the same state of its input. The thermostat will switch back from manual to automatic mode after a defined time where the input is inactive. The following parameter can be set to modify the time window in minutes to switch back to automatic in case the input is inactive:

      cmnd/Tasmota_Name/TIMEMANUALTOAUTOSET 60
      +

      The default value for the time window to switch from manual to automatic is 60 minutes.

      Temperature input / setpoint~

      Local temperature sensor~

      The tasmota driver can receive the temperature either via the related MQTT command or via a local temperature sensor (see example of a DS18B20 temperature sensor and a shelly temperature sensor addon below).

      Pinout

      The default temperature input is MQTT. The following command can be used to select the local sensor as default input:

      cmnd/Tasmota_Name/SENSORINPUTSET 1
      +

      Note

      The default local temperature sensor is a DS18B20. In case a different Tasmota supported sensor is used, the following define in my_user_config.h is to be changed (or redefined in user_config_override.h) and a user specific tasmota software needs to be compiled:

      #define THERMOSTAT_SENSOR_NAME                "DS18B20" // Name of the local sensor to be used
      +

      MQTT temperature value and setpoint~

      The following commands can be used to provide the driver with the temperature value of the room and the desired setpoint:

      cmnd/Tasmota_Name/TEMPTARGETSET 22.5
      +
      cmnd/Tasmota_Name/TEMPMEASUREDSET 21.8
      +

      Examples for room temperature of 21.8°C and temperature setpoint of 22.5°C

      There are several ways to send the MQTT room temperature. For the development and testing of this driver, a Raspberry Pi with Domoticz and a Z-Wave stick has been used to gather all room temperatures from Z-Wave sensors and send them to the respective Tasmota room thermostats.

      Customization of the controller for best results~

      The thermostat controller includes a default parameter set that targets a typical floor heating application for mid-sized rooms (< 20m2) with one heating circuit. The controller is however highly configurable via MQTT. The following sections will guide the user to adapt the main parameters to improve the performance of the thermostat controller via customization.

      Enable the thermostat driver~

      The thermostat driver is by default disabled. To enable it the following command can be used:

      cmnd/Tasmota_Name/THERMOSTATMODESET 1
      +

      Set the controller in degrees Celsius or Fahrenheit~

      The default temperature format is degrees Celsius. The format can be easily switched to degrees Fahrenheit via MQTT command, see below:

      cmnd/Tasmota_Name/TEMPFORMATSET 1
      +

      Set the control strategy~

      The control strategy by default is a Hybrid one. The hybrid control strategy mixes a so called "Ramp-Up" strategy (invention during the development of this driver, initially as a LUA script and ported to Tasmota later) and a PI one. The "Ramp-Up" strategy is typically used to reach as fast as possible the setpoint for big deltas between desired temperature and setpoint, the PI on the other hand for most part of the normal thermostat operation.

      The control strategy can be however modified, if the Hybrid one is not desired, the PI or "Ramp-Up" mode, can be forced. For that purpose the following command can be used:

      cmnd/Tasmota_Name/CONTROLLERMODESET 1
      +

      The value 1 forces the PI operation and the value 2 the "Ramp-Up" one.

      PI controller main parameters~

      Cycle time~

      Depending on the heating system, the cycle time (PMW period) can be adapted. Very slow systems (high time constants) such as heating floor systems might need higher values (default value is 30 minutes), faster systems might need smaller cycle times. Below the command to adapt the cycle time can be found:

      cmnd/Tasmota_Name/TIMEPICYCLESET 30
      +

      Proportional Band~

      Depending on the dimensioning of your heating system, the proportional band of the controller might be increased (if it takes too long to reach setpoint) or reduced (very high overshoot). The default proportional gain is 4, which means that the duty cycle due to the proportional part of the PI controller will be 100% for temperature deltas between setpoint and room temperature equal or bigger than 4°C. Below the command to adapt the proportional band can be found:

      cmnd/Tasmota_Name/PROPBANDSET 1
      +

      Note

      With the command above, the PI controller will output a proportional time equivalent to 100% of the duty cycle for delta temperatures between setpoint and room temp. above 1°C (f.e. for big rooms with weak dimensioned heating circuit).

      Reset Time~

      The reset time is the time the PI controller takes to overcome steady-state errors. The default value for the reset time is 1800 seconds. This value can be for instance increased in case a stronger integral reaction of the controller is desired. Below the command to adapt the proportional band can be found:

      cmnd/Tasmota_Name/TIMERESETSET 1800
      +

      Temperature for the anti-windup reset~

      To avoid the accumulated error and therefore integral component of the PI controller to grow too much and produce a high overshoot, a temperature delta can be defined within the integrator will work. Outside this range the accumulated error and integral part will be set to 0. The default value for the integrator to work is 0.8°C. Below the command to adapt the anti-windup temperature can be found:

      cmnd/Tasmota_Name/TEMPANTIWINDUPRESETSET 0.8
      +

      Temperature hysteresis~

      A temperature hysteresis can be set to avoid any PI controller actions within a certain value around the setpoint. The default value for the hysteresis is 0.1°C. In well configured controller this value should be as low as possible to avoid unwanted temperature oscillations which reduce efficiency and therefore increase costs. Below the command to adapt the anti-windup temperature can be found:

      cmnd/Tasmota_Name/TEMPHYSTSET 0.1
      +

      Maximum action of the controller~

      The maximum On time (Duty Cycle) in minutes within a cycle can be set by this parameter. The default value is 20 minutes. This represents for the default cycle time of 30 minutes 2 thirds of the complete cycle. In case the controller takes too long to reach the setpoint, this value can be increased to values closer to the cycle time. Below the command to adapt the maximum action time can be found:

      cmnd/Tasmota_Name/TIMEMAXACTIONSET 20
      +

      Minimum action of the controller~

      The minimum On time (Duty Cycle) in minutes within a cycle can be set by this parameter. The default value is 4 minutes. Below the command to adapt the minimum action time can be found:

      cmnd/Tasmota_Name/TIMEMINACTIONSET 4
      +

      It is very important to adapt this value to your heating system to obtain accurate temperature control

      If the value is very low, in case of floor heating systems for instance, the heating actuators might not have enough time to open the valves and the temperature will drop (depending on the actuator open/close time could take from 1 to 3 minutes) if it is too high, there will be unwanted oscillations around the setpoint. One way to configure this value in heating mode is to manually tune it in worst case conditions (highest typically desired room temperature and lower winter temperature outside) checking that the proportional action generated by the controller is sufficient to raise slightly the temperature. If the temperature still goes down after the pulse plus delay time of the system and rises just once the accumulated error triggers integral actions then the value set is too low.

      Ramp-Up controller main parameters~

      Temperature delta to get into "Ramp-Up" mode~

      When the controller is configured in Hybrid mode (default), the control strategy will be a mix between "Ramp-Up" (for big deltas between room temperature and setpoint) and PI (around the setpoint). The following parameter can be set to define the delta temperature (between measured and setpoint) above which the "Ramp-Up" controller will be active:

      cmnd/Tasmota_Name/TEMPRUPDELTINSET 0.3
      +

      The default value is 0.4°C.

      Time passed after latest setpoint change to get into "Ramp-Up" mode~

      When the controller is configured in Hybrid mode (default), the activation of the "Ramp-Up" mode will not just depend on the defined temperature delta between measured and setpoint, but as well on the time in minutes passed since the last setpoint change occurred. This strategy matches the purpose of the "Ramp-Up" controller, which was developed to reach the desired temperature as fast as possible in very specific scenarios, f.e. after a night keeping the room temperature low. In hybrid mode, the controller active most part of the time should be the PI one. The following parameter can be used to define the time to allow switching to "Ramp-Up" in minutes.

      cmnd/Tasmota_Name/TIMEALLOWRAMPUPSET 300
      +

      The default value is 300 minutes.

      Cycle time~

      Depending on the heating system, the cycle time (PMW period) can be adapted. Very slow systems (high time constants) such as heating floor systems might need higher values (default value is 30 minutes), faster systems might need smaller cycle times. The following parameter can be used to define the cycle time in minutes:

      cmnd/Tasmota_Name/TIMERAMPUPCYCLESET 45
      +

      Maximum Ramp-Up time~

      The maximum time the ramp-up phase of the controller shall be active can be configured (default value is 960 minutes). The following parameter can be used to define the ramp-up time in minutes:

      cmnd/Tasmota_Name/TIMERAMPUPMAXSET 180
      +

      Thermostat persistent storage for configuration~

      The thermostat driver stores all configured parameters over MQTT exclusively in RAM, it does not use flash due to the amount of the parameters. This means that at every restart the default parameters will be set again. To avoid this behavior rules can be set-up to reconfigure desired parameters at every restart. See below an example:

      ON Power1#boot DO Backlog sensorinputset 1;controllermodeset 2;thermostatmodeset 1;temptargetset %mem1% ENDON
      +ON mqtt#connected DO Publish2 stat/TestTopic/targetTempValue {"Temp":%mem1%} ENDON
      +ON mem1#state DO Backlog temptargetset %value%;Publish2 stat/TestTopic/targetTempValue {"Temp":%mem1%} ENDON
      +

      Detecting an opened window by temperature drop~

      When opening windows, temperature will drop significantly faster than is normal. During that period, heating is usually not desired. Following rules will detect a .3C drop in 1min and send the thermostat into a 15min timeout. Replace HTU21 with your sensor's name, if your Teleperiod is < 1min, consider using Tele-HTU21#Temperature.

      ON Event#windowstop DO Backlog EnableOutputSet 0; Power 0; RuleTimer1 900 ENDON
      +ON Rules#Timer=1 DO EnableOutputSet 1 ENDON
      +ON HTU21#Temperature DO Var1 %value% ENDON
      +ON Time#Minute DO Backlog Var3 %Var2%; Sub3 .3; Var2 %Var1% ENDON
      +ON Var2#State<=%Var3% DO Event windowstop ENDON
      +

      Advanced features~

      Multi-controller~

      The tasmota driver can be compiled to be used in devices with more than one output, allowing independent controllers for each one of the outputs. This feature has been successfully tested with a Sonoff 4CH PRO R2.

      Pinout

      To increase the number of controller outputs, modify the value of the thermostat controller outputs in my_user_config.h or redefine it in user_config_override.h and compile a customized tasmota software.

      #define THERMOSTAT_CONTROLLER_OUTPUTS         1         // Number of outputs to be controlled independently
      +

      Alternative outputs: PWM duty cycle~

      The driver provides the possibility to read the duty cycle in % (0-100) of the actuated relay. Below the command to read the duty cycle can be found:

      cmnd/Tasmota_Name/CTRDUTYCYCLEREAD
      +

      The physical switch of the output can as well be disabled via command. Below the command to disable it can be found:

      cmnd/Tasmota_Name/ENABLEOUTPUTSET 0
      +

      Future improvements~

      Cooling~

      The controller offers the possibility to switch from heating to cooling. Due to lack of cooling setup at the time of the development of the driver, this feature has however not been properly tested. Testers for cooling are therefore welcomed.

      The following MQTT command can be used to switch from heating (default) to cooling:

      cmnd/Tasmota_Name/CLIMATEMODESET 1
      +

      Self learning process of the "Ramp-Up" controller to reduce overshoot~

      The "Ramp-Up" controller evaluates the time constant of the system and predicts when to switch off the actuator to reach the desired temperature as fast as possible. This controller offers the best speed to reach the Setpoint. This controller will be improved by a learning process to evaluate how accurate the target value has been reached and therefore minimize gradually the overshoot. This feature will improve the behavior of the current controller which depending on the application and thermal capacity of the system might produce some overshoot. By default the controller set is the Hybrid one, enabling "Ramp-Up" for big temperature deltas between Setpoint and measured temperature and PI for smaller ones. If you are not satisfied with the performance of this controller in your system, you can disable it by MQTT and force the use of the PI controller exclusively (see Controller configuration section above).

      PI Autotune~

      A PI autotune feature following the Zigler-Nichols closed loop algorithm has been implemented. This feature is untested and will be further developed soon. To enable it for testing purposes add the following define in user_config_override.h and compile a customized tasmota software.

      #define USE_PI_AUTOTUNING // (Ziegler-Nichols closed loop method)
      +

      Example~

      The following chart shows the thermostat behavior in a forced air heated house:

      Thermostat

      The red shaded areas are where the thermostat commanded heat and the green line is the temperature. The temperature setpoint is 18 C at night and is then increased to 22.5 in the morning. This step demonstrates the ramp part of the hybrid control algorithm of the thermostat. Once the temperature is near the set point the thermostat switches to he PI algorithm. In the evenings the temperature is increased to 23.5 C.

      \ No newline at end of file diff --git a/Time-Proportioned-Output-support/index.html b/Time-Proportioned-Output-support/index.html new file mode 100644 index 0000000000..dbe9519b09 --- /dev/null +++ b/Time-Proportioned-Output-support/index.html @@ -0,0 +1 @@ + Time Proportioned Output support - Tasmota

      Time Proportioned Output support

      This extension adds a Time Proportioned Digital Output feature into the Tasmota software.

      The relay output on a Sonoff device provides (obviously) just on/off control. Often it is desirable to be able to get a value between off and on, for example 25% power. The conventional way to achieve this with devices such as electrical heaters or hot water radiators is to switch the device on for a period and then off for a period. This extension allows a required power value between 0 and 1 to be specified via MQTT and the code will automatically cycle the relay on/off to achieve this power. The s/w is configured with a Cycle Time that specifies the period. So for example if a power value of 0.25 is specified with a cycle time of 12 minutes then the relay will be on for 3 minutes and off for 9 minutes every 12 minute period.

      The s/w includes a safety mechanism where the maximum time expected between MQTT power updates is specified. If this time is exceeded with no value being received then the power will revert to a specified fallback value.

      Instructions for setting it up and using it are in xdrv_48_timeprop.ino. The feature adds about 1.2k to the compiled code.

      Currently all configuration parameters must be setup at build time. If anyone wanted to add these to the MQTT and/or web interfaces a PR would be gratefully received.

      \ No newline at end of file diff --git a/Timers/index.html b/Timers/index.html new file mode 100644 index 0000000000..be1770ec67 --- /dev/null +++ b/Timers/index.html @@ -0,0 +1,4 @@ + Timers - Tasmota
      Skip to content

      Timers

      Timers allow you to automate your device based on time triggers

      To control a device locally 16 timers are programmable. They can be configured with the Timer<x> command followed by a JSON payload with optional parameters. For example:

      Timer 1 will ENABLE output of POWER1 at exactly 2:23 every Tue/Wed and Sat

      Timer1 {"Enable":1,"Time":"02:23","Window":0,"Days":"--TW--S","Repeat":1,"Output":1,"Action":1}
      +

      Timer 4 will TOGGLE output of POWER2 within a 30 minute window centered around 16:23 each Sunday, Monday, Thursday and Friday and will disable (disarm) after executing.

      Timer4 {"Enable":1,"Time":"16:23","Window":15,"Days":"SM00TF0","Repeat":0,"Output":2,"Action":2}
      +

      When Mode 1 or Mode 2 is used, Latitude and Longitude are required. In that case the Time value is always used as an offset from sunrise or sunset, so make sure to set it to 00:00 if no offset is wanted. Timer 1 will ENABLE output of POWER1 2 hours and 23 minutes before sunset every day of the week.

      Timer1 {"Enable":1,"Mode":2,"Time":"-2:23","Window":0,"Days":"11TW11S","Repeat":1,"Output":1,"Action":1}
      +

      Commands~

      Command Parameters
      Latitude <value> = set latitude
      Longitude <value> = set longitude
      Sunrise Sunrise/Sunset type
      0 = Normal (default)
      1 = Civil
      2 = Nautical
      3 = Astronomical
      introduced in version 12.1.1.5
      Timers Timers control
      0 = disable all timers  » v6.2.0
      1 = enable all timers
      2 = toggle all timers
      Timer<x> Parameters for Timer<x> where x = 1..16
      0 = clear parameters for Timer<x>  » v6.2.0
      1..16 = copy Timer<y> parameters to Timer<x>
      { "name":value ; .. } = set all or individual parameters using JSON payload with names and values of data pairs from the table below

      JSON Payload Anatomy~

      JSON Name JSON Value
      Enable 0 = disarm or disable timer
      1 = arm or enable timer
      Mode 0 = use clock time
      1 = Use local sunrise time using Longitude, Latitude and Time offset
      2 = use local sunset time using Longitude, Latitude and Time offset
      Time hh:mm = set time in hours 0 .. 23 and minutes 0 .. 59
      -hh:mm = set time in offset hours -11 .. 12 and minutes 0 .. 59 (used with Mode 1 and Mode 2)
      Window 0..15 = add or subtract a random number of minutes to Time
      Days SMTWTFS = set day of weeks mask where 0 or - = OFF and any different character = ON
      Repeat 0 = allow timer only once
      1 = repeat timer execution
      Output 1..16 = select an output to be used if no rule is enabled
      Action 0 = turn output OFF
      1 = turn output ON
      2 = TOGGLE output
      3 = RULE/BLINK
      If the Tasmota Rules feature has been activated by compiling the code (activated by default in all pre-compiled Tasmota binaries), a rule with Clock#Timer=<timer> will be triggered if written and turned on by the user.
      If Rules are not compiled, BLINK output using BlinkCount parameters.

      Sunrise~

      In order to set a timer to use the automatically calculated sunrise or sunset time, the latitude and longitude of the location as decimal degrees needs to be entered using the respective commands.

      By default, Tasmota calculates the times of the actual sunrise and sunset. Starting with version 12.1.1.5, it is possible instead to use the beginning and ending times of civil, nautical or astronomical twilight. In particular, civil twilight is the period of time when the sun is below the horizon, but artificial lighting is not yet needed.

      \ No newline at end of file diff --git a/Timezone-Table/index.html b/Timezone-Table/index.html new file mode 100644 index 0000000000..5bb54dec95 --- /dev/null +++ b/Timezone-Table/index.html @@ -0,0 +1 @@ + Timezone Table - Tasmota
      Skip to content

      Tasmota Timezone Table~

      Use this table to look up the correct Timezone, TimeStd, and TimeDst commands to configure a Tasmota device for your local timezone.

      Timezone Commands
      Africa/Abidjan Timezone +0:00
      Africa/Accra Timezone +0:00
      Africa/Addis_Ababa Timezone +3:00
      Africa/Algiers Timezone +1:00
      Africa/Asmara Timezone +3:00
      Africa/Asmera Timezone +3:00
      Africa/Bamako Timezone +0:00
      Africa/Bangui Timezone +1:00
      Africa/Banjul Timezone +0:00
      Africa/Bissau Timezone +0:00
      Africa/Blantyre Timezone +2:00
      Africa/Brazzaville Timezone +1:00
      Africa/Bujumbura Timezone +2:00
      Africa/Cairo Timezone +2:00
      Africa/Casablanca Timezone +1:00
      Africa/Ceuta Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Africa/Conakry Timezone +0:00
      Africa/Dakar Timezone +0:00
      Africa/Dar_es_Salaam Timezone +3:00
      Africa/Djibouti Timezone +3:00
      Africa/Douala Timezone +1:00
      Africa/El_Aaiun Timezone +1:00
      Africa/Freetown Timezone +0:00
      Africa/Gaborone Timezone +2:00
      Africa/Harare Timezone +2:00
      Africa/Johannesburg Timezone +2:00
      Africa/Juba Timezone +2:00
      Africa/Kampala Timezone +3:00
      Africa/Khartoum Timezone +2:00
      Africa/Kigali Timezone +2:00
      Africa/Kinshasa Timezone +1:00
      Africa/Lagos Timezone +1:00
      Africa/Libreville Timezone +1:00
      Africa/Lome Timezone +0:00
      Africa/Luanda Timezone +1:00
      Africa/Lubumbashi Timezone +2:00
      Africa/Lusaka Timezone +2:00
      Africa/Malabo Timezone +1:00
      Africa/Maputo Timezone +2:00
      Africa/Maseru Timezone +2:00
      Africa/Mbabane Timezone +2:00
      Africa/Mogadishu Timezone +3:00
      Africa/Monrovia Timezone +0:00
      Africa/Nairobi Timezone +3:00
      Africa/Ndjamena Timezone +1:00
      Africa/Niamey Timezone +1:00
      Africa/Nouakchott Timezone +0:00
      Africa/Ouagadougou Timezone +0:00
      Africa/Porto-Novo Timezone +1:00
      Africa/Sao_Tome Timezone +0:00
      Africa/Timbuktu Timezone +0:00
      Africa/Tripoli Timezone +2:00
      Africa/Tunis Timezone +1:00
      Africa/Windhoek Timezone +2:00
      America/Adak Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-600; TimeDst 0,2,3,1,2,-540
      America/Anchorage Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-540; TimeDst 0,2,3,1,2,-480
      America/Anguilla Timezone -4:00
      America/Antigua Timezone -4:00
      America/Araguaina Timezone -3:00
      America/Argentina/Buenos_Aires Timezone -3:00
      America/Argentina/Catamarca Timezone -3:00
      America/Argentina/ComodRivadavia Timezone -3:00
      America/Argentina/Cordoba Timezone -3:00
      America/Argentina/Jujuy Timezone -3:00
      America/Argentina/La_Rioja Timezone -3:00
      America/Argentina/Mendoza Timezone -3:00
      America/Argentina/Rio_Gallegos Timezone -3:00
      America/Argentina/Salta Timezone -3:00
      America/Argentina/San_Juan Timezone -3:00
      America/Argentina/San_Luis Timezone -3:00
      America/Argentina/Tucuman Timezone -3:00
      America/Argentina/Ushuaia Timezone -3:00
      America/Aruba Timezone -4:00
      America/Asuncion Backlog0 Timezone 99; TimeStd 1,4,3,1,0,-240; TimeDst 1,1,10,1,0,-180
      America/Atikokan Timezone -5:00
      America/Atka Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-600; TimeDst 0,2,3,1,2,-540
      America/Bahia Timezone -3:00
      America/Bahia_Banderas Timezone -6:00
      America/Barbados Timezone -4:00
      America/Belem Timezone -3:00
      America/Belize Timezone -6:00
      America/Blanc-Sablon Timezone -4:00
      America/Boa_Vista Timezone -4:00
      America/Bogota Timezone -5:00
      America/Boise Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      America/Buenos_Aires Timezone -3:00
      America/Cambridge_Bay Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      America/Campo_Grande Timezone -4:00
      America/Cancun Timezone -5:00
      America/Caracas Timezone -4:00
      America/Catamarca Timezone -3:00
      America/Cayenne Timezone -3:00
      America/Cayman Timezone -5:00
      America/Chicago Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Chihuahua Timezone -6:00
      America/Ciudad_Juarez Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      America/Coral_Harbour Timezone -5:00
      America/Cordoba Timezone -3:00
      America/Costa_Rica Timezone -6:00
      America/Creston Timezone -7:00
      America/Cuiaba Timezone -4:00
      America/Curacao Timezone -4:00
      America/Danmarkshavn Timezone +0:00
      America/Dawson Timezone -7:00
      America/Dawson_Creek Timezone -7:00
      America/Denver Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      America/Detroit Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Dominica Timezone -4:00
      America/Edmonton Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      America/Eirunepe Timezone -5:00
      America/El_Salvador Timezone -6:00
      America/Ensenada Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-480; TimeDst 0,2,3,1,2,-420
      America/Fort_Nelson Timezone -7:00
      America/Fort_Wayne Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Fortaleza Timezone -3:00
      America/Glace_Bay Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-240; TimeDst 0,2,3,1,2,-180
      America/Godthab Timezone -2:00
      America/Goose_Bay Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-240; TimeDst 0,2,3,1,2,-180
      America/Grand_Turk Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Grenada Timezone -4:00
      America/Guadeloupe Timezone -4:00
      America/Guatemala Timezone -6:00
      America/Guayaquil Timezone -5:00
      America/Guyana Timezone -4:00
      America/Halifax Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-240; TimeDst 0,2,3,1,2,-180
      America/Havana Backlog0 Timezone 99; TimeStd 0,1,11,1,1,-300; TimeDst 0,2,3,1,0,-240
      America/Hermosillo Timezone -7:00
      America/Indiana/Indianapolis Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Indiana/Knox Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Indiana/Marengo Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Indiana/Petersburg Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Indiana/Tell_City Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Indiana/Vevay Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Indiana/Vincennes Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Indiana/Winamac Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Indianapolis Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Inuvik Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      America/Iqaluit Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Jamaica Timezone -5:00
      America/Jujuy Timezone -3:00
      America/Juneau Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-540; TimeDst 0,2,3,1,2,-480
      America/Kentucky/Louisville Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Kentucky/Monticello Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Knox_IN Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Kralendijk Timezone -4:00
      America/La_Paz Timezone -4:00
      America/Lima Timezone -5:00
      America/Los_Angeles Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-480; TimeDst 0,2,3,1,2,-420
      America/Louisville Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Lower_Princes Timezone -4:00
      America/Maceio Timezone -3:00
      America/Managua Timezone -6:00
      America/Manaus Timezone -4:00
      America/Marigot Timezone -4:00
      America/Martinique Timezone -4:00
      America/Matamoros Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Mazatlan Timezone -7:00
      America/Mendoza Timezone -3:00
      America/Menominee Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Merida Timezone -6:00
      America/Metlakatla Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-540; TimeDst 0,2,3,1,2,-480
      America/Mexico_City Timezone -6:00
      America/Miquelon Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-180; TimeDst 0,2,3,1,2,-120
      America/Moncton Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-240; TimeDst 0,2,3,1,2,-180
      America/Monterrey Timezone -6:00
      America/Montevideo Timezone -3:00
      America/Montreal Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Montserrat Timezone -4:00
      America/Nassau Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/New_York Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Nipigon Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Nome Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-540; TimeDst 0,2,3,1,2,-480
      America/Noronha Timezone -2:00
      America/North_Dakota/Beulah Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/North_Dakota/Center Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/North_Dakota/New_Salem Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Nuuk Timezone -2:00
      America/Ojinaga Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Panama Timezone -5:00
      America/Pangnirtung Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Paramaribo Timezone -3:00
      America/Phoenix Timezone -7:00
      America/Port-au-Prince Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Port_of_Spain Timezone -4:00
      America/Porto_Acre Timezone -5:00
      America/Porto_Velho Timezone -4:00
      America/Puerto_Rico Timezone -4:00
      America/Punta_Arenas Timezone -3:00
      America/Rainy_River Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Rankin_Inlet Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Recife Timezone -3:00
      America/Regina Timezone -6:00
      America/Resolute Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Rio_Branco Timezone -5:00
      America/Rosario Timezone -3:00
      America/Santa_Isabel Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-480; TimeDst 0,2,3,1,2,-420
      America/Santarem Timezone -3:00
      America/Santiago This timezone uses a DST start/end rule that Tasmota does not support.
      America/Santo_Domingo Timezone -4:00
      America/Sao_Paulo Timezone -3:00
      America/Scoresbysund Backlog0 Timezone 99; TimeStd 0,0,10,1,1,-60; TimeDst 0,0,3,1,0,0
      America/Shiprock Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      America/Sitka Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-540; TimeDst 0,2,3,1,2,-480
      America/St_Barthelemy Timezone -4:00
      America/St_Johns Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-210.0; TimeDst 0,2,3,1,2,-150.0
      America/St_Kitts Timezone -4:00
      America/St_Lucia Timezone -4:00
      America/St_Thomas Timezone -4:00
      America/St_Vincent Timezone -4:00
      America/Swift_Current Timezone -6:00
      America/Tegucigalpa Timezone -6:00
      America/Thule Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-240; TimeDst 0,2,3,1,2,-180
      America/Thunder_Bay Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Tijuana Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-480; TimeDst 0,2,3,1,2,-420
      America/Toronto Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      America/Tortola Timezone -4:00
      America/Vancouver Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-480; TimeDst 0,2,3,1,2,-420
      America/Virgin Timezone -4:00
      America/Whitehorse Timezone -7:00
      America/Winnipeg Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      America/Yakutat Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-540; TimeDst 0,2,3,1,2,-480
      America/Yellowknife Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      Antarctica/Casey Timezone +11:00
      Antarctica/Davis Timezone +7:00
      Antarctica/DumontDUrville Timezone +10:00
      Antarctica/Macquarie Backlog0 Timezone 99; TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660
      Antarctica/Mawson Timezone +5:00
      Antarctica/McMurdo Backlog0 Timezone 99; TimeStd 1,1,4,1,3,720; TimeDst 1,0,9,1,2,780
      Antarctica/Palmer Timezone -3:00
      Antarctica/Rothera Timezone -3:00
      Antarctica/South_Pole Backlog0 Timezone 99; TimeStd 1,1,4,1,3,720; TimeDst 1,0,9,1,2,780
      Antarctica/Syowa Timezone +3:00
      Antarctica/Troll Backlog0 Timezone 99; TimeStd 0,0,10,1,3,0; TimeDst 0,0,3,1,1,120
      Antarctica/Vostok Timezone +6:00
      Arctic/Longyearbyen Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Asia/Aden Timezone +3:00
      Asia/Almaty Timezone +6:00
      Asia/Amman Timezone +3:00
      Asia/Anadyr Timezone +12:00
      Asia/Aqtau Timezone +5:00
      Asia/Aqtobe Timezone +5:00
      Asia/Ashgabat Timezone +5:00
      Asia/Ashkhabad Timezone +5:00
      Asia/Atyrau Timezone +5:00
      Asia/Baghdad Timezone +3:00
      Asia/Bahrain Timezone +3:00
      Asia/Baku Timezone +4:00
      Asia/Bangkok Timezone +7:00
      Asia/Barnaul Timezone +7:00
      Asia/Beirut Backlog0 Timezone 99; TimeStd 0,0,10,1,0,120; TimeDst 0,0,3,1,0,180
      Asia/Bishkek Timezone +6:00
      Asia/Brunei Timezone +8:00
      Asia/Calcutta Timezone +5:30
      Asia/Chita Timezone +9:00
      Asia/Choibalsan Timezone +8:00
      Asia/Chongqing Timezone +8:00
      Asia/Chungking Timezone +8:00
      Asia/Colombo Timezone +5:30
      Asia/Dacca Timezone +6:00
      Asia/Damascus Timezone +3:00
      Asia/Dhaka Timezone +6:00
      Asia/Dili Timezone +9:00
      Asia/Dubai Timezone +4:00
      Asia/Dushanbe Timezone +5:00
      Asia/Famagusta Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Asia/Gaza This timezone uses a DST start/end rule that Tasmota does not support.
      Asia/Harbin Timezone +8:00
      Asia/Hebron This timezone uses a DST start/end rule that Tasmota does not support.
      Asia/Ho_Chi_Minh Timezone +7:00
      Asia/Hong_Kong Timezone +8:00
      Asia/Hovd Timezone +7:00
      Asia/Irkutsk Timezone +8:00
      Asia/Istanbul Timezone +3:00
      Asia/Jakarta Timezone +7:00
      Asia/Jayapura Timezone +9:00
      Asia/Jerusalem This timezone uses a DST start/end rule that Tasmota does not support.
      Asia/Kabul Timezone +4:30
      Asia/Kamchatka Timezone +12:00
      Asia/Karachi Timezone +5:00
      Asia/Kashgar Timezone +6:00
      Asia/Kathmandu Timezone +5:45
      Asia/Katmandu Timezone +5:45
      Asia/Khandyga Timezone +9:00
      Asia/Kolkata Timezone +5:30
      Asia/Krasnoyarsk Timezone +7:00
      Asia/Kuala_Lumpur Timezone +8:00
      Asia/Kuching Timezone +8:00
      Asia/Kuwait Timezone +3:00
      Asia/Macao Timezone +8:00
      Asia/Macau Timezone +8:00
      Asia/Magadan Timezone +11:00
      Asia/Makassar Timezone +8:00
      Asia/Manila Timezone +8:00
      Asia/Muscat Timezone +4:00
      Asia/Nicosia Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Asia/Novokuznetsk Timezone +7:00
      Asia/Novosibirsk Timezone +7:00
      Asia/Omsk Timezone +6:00
      Asia/Oral Timezone +5:00
      Asia/Phnom_Penh Timezone +7:00
      Asia/Pontianak Timezone +7:00
      Asia/Pyongyang Timezone +9:00
      Asia/Qatar Timezone +3:00
      Asia/Qostanay Timezone +6:00
      Asia/Qyzylorda Timezone +5:00
      Asia/Rangoon Timezone +6:30
      Asia/Riyadh Timezone +3:00
      Asia/Saigon Timezone +7:00
      Asia/Sakhalin Timezone +11:00
      Asia/Samarkand Timezone +5:00
      Asia/Seoul Timezone +9:00
      Asia/Shanghai Timezone +8:00
      Asia/Singapore Timezone +8:00
      Asia/Srednekolymsk Timezone +11:00
      Asia/Taipei Timezone +8:00
      Asia/Tashkent Timezone +5:00
      Asia/Tbilisi Timezone +4:00
      Asia/Tehran Timezone +3:30
      Asia/Tel_Aviv This timezone uses a DST start/end rule that Tasmota does not support.
      Asia/Thimbu Timezone +6:00
      Asia/Thimphu Timezone +6:00
      Asia/Tokyo Timezone +9:00
      Asia/Tomsk Timezone +7:00
      Asia/Ujung_Pandang Timezone +8:00
      Asia/Ulaanbaatar Timezone +8:00
      Asia/Ulan_Bator Timezone +8:00
      Asia/Urumqi Timezone +6:00
      Asia/Ust-Nera Timezone +10:00
      Asia/Vientiane Timezone +7:00
      Asia/Vladivostok Timezone +10:00
      Asia/Yakutsk Timezone +9:00
      Asia/Yangon Timezone +6:30
      Asia/Yekaterinburg Timezone +5:00
      Asia/Yerevan Timezone +4:00
      Atlantic/Azores Backlog0 Timezone 99; TimeStd 0,0,10,1,1,-60; TimeDst 0,0,3,1,0,0
      Atlantic/Bermuda Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-240; TimeDst 0,2,3,1,2,-180
      Atlantic/Canary Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Atlantic/Cape_Verde Timezone -1:00
      Atlantic/Faeroe Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Atlantic/Faroe Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Atlantic/Jan_Mayen Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Atlantic/Madeira Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Atlantic/Reykjavik Timezone +0:00
      Atlantic/South_Georgia Timezone -2:00
      Atlantic/St_Helena Timezone +0:00
      Atlantic/Stanley Timezone -3:00
      Australia/ACT Backlog0 Timezone 99; TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660
      Australia/Adelaide Backlog0 Timezone 99; TimeStd 1,1,4,1,3,570.0; TimeDst 1,1,10,1,2,630.0
      Australia/Brisbane Timezone +10:00
      Australia/Broken_Hill Backlog0 Timezone 99; TimeStd 1,1,4,1,3,570.0; TimeDst 1,1,10,1,2,630.0
      Australia/Canberra Backlog0 Timezone 99; TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660
      Australia/Currie Backlog0 Timezone 99; TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660
      Australia/Darwin Timezone +9:30
      Australia/Eucla Timezone +8:45
      Australia/Hobart Backlog0 Timezone 99; TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660
      Australia/LHI Backlog0 Timezone 99; TimeStd 1,1,4,1,2,630.0; TimeDst 1,1,10,1,2,660
      Australia/Lindeman Timezone +10:00
      Australia/Lord_Howe Backlog0 Timezone 99; TimeStd 1,1,4,1,2,630.0; TimeDst 1,1,10,1,2,660
      Australia/Melbourne Backlog0 Timezone 99; TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660
      Australia/NSW Backlog0 Timezone 99; TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660
      Australia/North Timezone +9:30
      Australia/Perth Timezone +8:00
      Australia/Queensland Timezone +10:00
      Australia/South Backlog0 Timezone 99; TimeStd 1,1,4,1,3,570.0; TimeDst 1,1,10,1,2,630.0
      Australia/Sydney Backlog0 Timezone 99; TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660
      Australia/Tasmania Backlog0 Timezone 99; TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660
      Australia/Victoria Backlog0 Timezone 99; TimeStd 1,1,4,1,3,600; TimeDst 1,1,10,1,2,660
      Australia/West Timezone +8:00
      Australia/Yancowinna Backlog0 Timezone 99; TimeStd 1,1,4,1,3,570.0; TimeDst 1,1,10,1,2,630.0
      Brazil/Acre Timezone -5:00
      Brazil/DeNoronha Timezone -2:00
      Brazil/East Timezone -3:00
      Brazil/West Timezone -4:00
      CET Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      CST6CDT Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      Canada/Atlantic Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-240; TimeDst 0,2,3,1,2,-180
      Canada/Central Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      Canada/Eastern Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      Canada/Mountain Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      Canada/Newfoundland Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-210.0; TimeDst 0,2,3,1,2,-150.0
      Canada/Pacific Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-480; TimeDst 0,2,3,1,2,-420
      Canada/Saskatchewan Timezone -6:00
      Canada/Yukon Timezone -7:00
      Chile/Continental This timezone uses a DST start/end rule that Tasmota does not support.
      Chile/EasterIsland Backlog0 Timezone 99; TimeStd 1,1,4,7,22,-360; TimeDst 1,1,9,7,22,-300
      Cuba Backlog0 Timezone 99; TimeStd 0,1,11,1,1,-300; TimeDst 0,2,3,1,0,-240
      EET Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      EST Timezone -5:00
      EST5EDT Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      Egypt Timezone +2:00
      Eire Backlog0 Timezone 99; TimeStd 1,0,3,1,1,60; TimeDst 1,0,10,1,2,0
      Etc/GMT Timezone +0:00
      Etc/GMT+0 Timezone +0:00
      Etc/GMT+1 Timezone -1:00
      Etc/GMT+10 Timezone -10:00
      Etc/GMT+11 Timezone -11:00
      Etc/GMT+12 Timezone -12:00
      Etc/GMT+2 Timezone -2:00
      Etc/GMT+3 Timezone -3:00
      Etc/GMT+4 Timezone -4:00
      Etc/GMT+5 Timezone -5:00
      Etc/GMT+6 Timezone -6:00
      Etc/GMT+7 Timezone -7:00
      Etc/GMT+8 Timezone -8:00
      Etc/GMT+9 Timezone -9:00
      Etc/GMT-0 Timezone +0:00
      Etc/GMT-1 Timezone +1:00
      Etc/GMT-10 Timezone +10:00
      Etc/GMT-11 Timezone +11:00
      Etc/GMT-12 Timezone +12:00
      Etc/GMT-13 Timezone +13:00
      Etc/GMT-14 This timezone has a UTC offset outside the range Tasmota supports.
      Etc/GMT-2 Timezone +2:00
      Etc/GMT-3 Timezone +3:00
      Etc/GMT-4 Timezone +4:00
      Etc/GMT-5 Timezone +5:00
      Etc/GMT-6 Timezone +6:00
      Etc/GMT-7 Timezone +7:00
      Etc/GMT-8 Timezone +8:00
      Etc/GMT-9 Timezone +9:00
      Etc/GMT0 Timezone +0:00
      Etc/Greenwich Timezone +0:00
      Etc/UCT Timezone +0:00
      Etc/UTC Timezone +0:00
      Etc/Universal Timezone +0:00
      Etc/Zulu Timezone +0:00
      Europe/Amsterdam Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Andorra Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Astrakhan Timezone +4:00
      Europe/Athens Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Belfast Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Europe/Belgrade Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Berlin Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Bratislava Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Brussels Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Bucharest Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Budapest Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Busingen Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Chisinau Backlog0 Timezone 99; TimeStd 0,0,10,1,3,120; TimeDst 0,0,3,1,2,180
      Europe/Copenhagen Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Dublin Backlog0 Timezone 99; TimeStd 1,0,3,1,1,60; TimeDst 1,0,10,1,2,0
      Europe/Gibraltar Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Guernsey Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Europe/Helsinki Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Isle_of_Man Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Europe/Istanbul Timezone +3:00
      Europe/Jersey Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Europe/Kaliningrad Timezone +2:00
      Europe/Kiev Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Kirov Timezone +3:00
      Europe/Kyiv Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Lisbon Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Europe/Ljubljana Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/London Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Europe/Luxembourg Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Madrid Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Malta Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Mariehamn Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Minsk Timezone +3:00
      Europe/Monaco Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Moscow Timezone +3:00
      Europe/Nicosia Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Oslo Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Paris Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Podgorica Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Prague Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Riga Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Rome Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Samara Timezone +4:00
      Europe/San_Marino Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Sarajevo Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Saratov Timezone +4:00
      Europe/Simferopol Timezone +3:00
      Europe/Skopje Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Sofia Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Stockholm Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Tallinn Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Tirane Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Tiraspol Backlog0 Timezone 99; TimeStd 0,0,10,1,3,120; TimeDst 0,0,3,1,2,180
      Europe/Ulyanovsk Timezone +4:00
      Europe/Uzhgorod Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Vaduz Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Vatican Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Vienna Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Vilnius Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Volgograd Timezone +3:00
      Europe/Warsaw Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Zagreb Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Europe/Zaporozhye Backlog0 Timezone 99; TimeStd 0,0,10,1,4,120; TimeDst 0,0,3,1,3,180
      Europe/Zurich Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Factory Timezone +0:00
      GB Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      GB-Eire Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      GMT Timezone +0:00
      GMT+0 Timezone +0:00
      GMT-0 Timezone +0:00
      GMT0 Timezone +0:00
      Greenwich Timezone +0:00
      HST Timezone -10:00
      Hongkong Timezone +8:00
      Iceland Timezone +0:00
      Indian/Antananarivo Timezone +3:00
      Indian/Chagos Timezone +6:00
      Indian/Christmas Timezone +7:00
      Indian/Cocos Timezone +6:30
      Indian/Comoro Timezone +3:00
      Indian/Kerguelen Timezone +5:00
      Indian/Mahe Timezone +4:00
      Indian/Maldives Timezone +5:00
      Indian/Mauritius Timezone +4:00
      Indian/Mayotte Timezone +3:00
      Indian/Reunion Timezone +4:00
      Iran Timezone +3:30
      Israel This timezone uses a DST start/end rule that Tasmota does not support.
      Jamaica Timezone -5:00
      Japan Timezone +9:00
      Kwajalein Timezone +12:00
      Libya Timezone +2:00
      MET Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      MST Timezone -7:00
      MST7MDT Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      Mexico/BajaNorte Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-480; TimeDst 0,2,3,1,2,-420
      Mexico/BajaSur Timezone -7:00
      Mexico/General Timezone -6:00
      NZ Backlog0 Timezone 99; TimeStd 1,1,4,1,3,720; TimeDst 1,0,9,1,2,780
      NZ-CHAT This timezone has a UTC offset outside the range Tasmota supports.
      Navajo Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      PRC Timezone +8:00
      PST8PDT Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-480; TimeDst 0,2,3,1,2,-420
      Pacific/Apia Timezone +13:00
      Pacific/Auckland Backlog0 Timezone 99; TimeStd 1,1,4,1,3,720; TimeDst 1,0,9,1,2,780
      Pacific/Bougainville Timezone +11:00
      Pacific/Chatham This timezone has a UTC offset outside the range Tasmota supports.
      Pacific/Chuuk Timezone +10:00
      Pacific/Easter Backlog0 Timezone 99; TimeStd 1,1,4,7,22,-360; TimeDst 1,1,9,7,22,-300
      Pacific/Efate Timezone +11:00
      Pacific/Enderbury Timezone +13:00
      Pacific/Fakaofo Timezone +13:00
      Pacific/Fiji Timezone +12:00
      Pacific/Funafuti Timezone +12:00
      Pacific/Galapagos Timezone -6:00
      Pacific/Gambier Timezone -9:00
      Pacific/Guadalcanal Timezone +11:00
      Pacific/Guam Timezone +10:00
      Pacific/Honolulu Timezone -10:00
      Pacific/Johnston Timezone -10:00
      Pacific/Kanton Timezone +13:00
      Pacific/Kiritimati This timezone has a UTC offset outside the range Tasmota supports.
      Pacific/Kosrae Timezone +11:00
      Pacific/Kwajalein Timezone +12:00
      Pacific/Majuro Timezone +12:00
      Pacific/Marquesas Timezone -9:30
      Pacific/Midway Timezone -11:00
      Pacific/Nauru Timezone +12:00
      Pacific/Niue Timezone -11:00
      Pacific/Norfolk Backlog0 Timezone 99; TimeStd 1,1,4,1,3,660; TimeDst 1,1,10,1,2,720
      Pacific/Noumea Timezone +11:00
      Pacific/Pago_Pago Timezone -11:00
      Pacific/Palau Timezone +9:00
      Pacific/Pitcairn Timezone -8:00
      Pacific/Pohnpei Timezone +11:00
      Pacific/Ponape Timezone +11:00
      Pacific/Port_Moresby Timezone +10:00
      Pacific/Rarotonga Timezone -10:00
      Pacific/Saipan Timezone +10:00
      Pacific/Samoa Timezone -11:00
      Pacific/Tahiti Timezone -10:00
      Pacific/Tarawa Timezone +12:00
      Pacific/Tongatapu Timezone +13:00
      Pacific/Truk Timezone +10:00
      Pacific/Wake Timezone +12:00
      Pacific/Wallis Timezone +12:00
      Pacific/Yap Timezone +10:00
      Poland Backlog0 Timezone 99; TimeStd 0,0,10,1,3,60; TimeDst 0,0,3,1,2,120
      Portugal Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      ROC Timezone +8:00
      ROK Timezone +9:00
      Singapore Timezone +8:00
      Turkey Timezone +3:00
      UCT Timezone +0:00
      US/Alaska Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-540; TimeDst 0,2,3,1,2,-480
      US/Aleutian Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-600; TimeDst 0,2,3,1,2,-540
      US/Arizona Timezone -7:00
      US/Central Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      US/East-Indiana Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      US/Eastern Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      US/Hawaii Timezone -10:00
      US/Indiana-Starke Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-360; TimeDst 0,2,3,1,2,-300
      US/Michigan Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-300; TimeDst 0,2,3,1,2,-240
      US/Mountain Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-420; TimeDst 0,2,3,1,2,-360
      US/Pacific Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-480; TimeDst 0,2,3,1,2,-420
      US/Samoa Timezone -11:00
      UTC Timezone +0:00
      Universal Timezone +0:00
      W-SU Timezone +3:00
      WET Backlog0 Timezone 99; TimeStd 0,0,10,1,2,0; TimeDst 0,0,3,1,1,60
      Zulu Timezone +0:00

      This table was generated from the IANA Time Zone Database, version 2022g.

      \ No newline at end of file diff --git a/Tips/index.html b/Tips/index.html new file mode 100644 index 0000000000..afecd2dd9a --- /dev/null +++ b/Tips/index.html @@ -0,0 +1 @@ + Tips - Tasmota
      \ No newline at end of file diff --git a/TouchPin/index.html b/TouchPin/index.html new file mode 100644 index 0000000000..eeebfa822b --- /dev/null +++ b/TouchPin/index.html @@ -0,0 +1 @@ + Touch GPIOs - Tasmota
      Skip to content

      Capacitive Touch GPIO ~

      The ESP32 has 10 capacitive touch GPIOs. It is possible to use a maximum number of 4 as a touch button.

      Note

      Only special pins are usable and not all of these 10 pins are exposed on every dev-board. More info in this article

      After wiring a cable or electrode to a supported pin you have to configure it in Configure Module as "button_tc".

      It is helpful to understand, what is going on under the hood:
      The continuous pin reading gives a unitless value, that will decrease when the pin (or connected cable) is touched. The touch pin driver will report a button touch when the pin reading falls below a threshold value for a certain amount of read cycles. The latter is important to filter out spikes.

      The default values are very conservative in order to rule out unwanted actions. In most cases it will be desirable to do a calibration.

      Commands:~

      Command Description
      TouchCal x x=button 1 .. 4. This plots the sensor values to the console, to get information regarding the setting of the 2 following commands
      0 will turn off calibration
      255 will turn on calibration for all buttons
      TouchThres x x=button 1 .. 4. This plots the sensor values to the console, to get information regarding the setting of the 2 following commands
      TouchNum x sets number of ignored measurements below the threshold, because there will likely be spikes. The default value of 3 is very conservative and 1 should be fine most of the time. A higher value is safer in a noisy environment, but for obvious reasons you will have to touch the pin (or cable ...) longer to trigger the button press.

      While the calibration process is running, the raw data values will be printed in the console in the format:

      g - number of the graph (= number of the button) v - raw value of the corresponding touch pin h - number of continuous hits below current threshold, useful to see the number and length of "spikes", should be 0 without touching

      The new values for a personal configuration can be stored in RULE:

      Example

      rule1 on System#Init do TouchNum 1 endon

      Tasmota Serial Plotter~

      This little tool should be helpful to get a feel for the touch values. It is located in the /tools folder of the Tasmota repository and needs the installation of "mathplotlib" and "pyserial" in the active python environment. It is confirmed to work under Windows 10 and macOS Catalina.

      Example: ./serial-plotter.py --port /dev/XXX --baud 115200

      You can send commands to Tasmota via SEND-box.

      For the touch button driver:

      TouchCal 255 - turns on calibration mode for all buttons

      (Note the short spikes, which in this config only would need TouchNum 1 to get filtered out)

      \ No newline at end of file diff --git a/Troubleshooting/index.html b/Troubleshooting/index.html new file mode 100644 index 0000000000..36fc43980f --- /dev/null +++ b/Troubleshooting/index.html @@ -0,0 +1,3 @@ + Troubleshooting - Tasmota
      Skip to content

      Troubleshooting~

      Debugging~

      Logs~

      For debugging purposes you can use Level 4 or Level 5 logging to the MqttLog, WebLog, SerialLog, or remote SysLog.

      The logging level is set separately for each log destination. Log levels range from 0 to 5. The higher the log level, the more information is logged. When troubleshooting your device it's recommended to set the logging level to 4.

      Web Logging~

      These show up in the Web UI Console (http://deviceip/cs). The default logging level for WebLog logging is 2.

      Serial Logging~

      Warning

      Never connect to serial while the device is connected to mains power. You can still collect the logs, but only when powering it via your serial connection.

      Some devices use the serial port to control the relays or an MCU, so serial logging might interfere with control and even switch relays or lights.

      The default logging level for SerialLog logging is 2. Unless explicitly set by a command (e.g., user input in the Console, a System#Boot triggered rule), SerialLog will be disabled automatically 10 minutes after the device reboots.

      Through a terminal program set the baud rate to 115200 (19200 for Sonoff Dual), both NL & CR, and disable hardware flow control.

      • Debugging the Sonoff Pow is a bit tricky as the serial interface has a direct connection to one of the AC power lines. The schematic below uses two optocouplers separating the AC connection on the left from the low voltage connection on the right allowing for serial control at 115200 baud and uploading of firmware up to 57600 baud while AC is connected. OptoSerial

      Crashdumps~

      If the ESP8266 crashes, it frequently dumps information about the crash out the serial port, so the process listed above to see serial logs can provide extremely useful information

      Syslog Logging~

      If you have a Linux system, it is probably already running syslog. You just need to configure it to listen on the network. SysLog logging is disabled in Tasmota by default.

      On systems running rsyslog (most linux distros), edit the /etc/rsyslog.conf file. Adding (or uncommenting) the following lines will probably start making the logs show up in some file under /var/log

      $ModLoad imudp  
      +$UDPServerRun 514  
      +

      If you do not have access to a Linux system, there are Microsoft Windows Syslog server options.

      MQTT Logging~

      These log messages show up as MQTT messages. MqttLog logging is disabled by default.

      MQTT traffic~

      To check the flow of MQTT traffic you can use MQTT Explorer which shows your entire MQTT traffic in an organised and structured way.

      Tasmota Device Manager offers an overview of all your Tasmota devices using MQTT protocol. You can manage them, use device features and do basic troubleshooting with ease. It also cleanly displays if your device is dropping from the network often or reboots unexpectedly.

      Running out of RAM~

      This typically shows up in the device working when it first starts up (hitting the button toggles the relay), but some time later it either reboots or some function won't work.

      For example, you can't load the module configuration page.

      The only fix for this is to recompile the firmware and disable features you don't need. Known large features are web server and TLS, but other things to consider disabling if you don't need them are emulation support, Domoticz support and WS8212 support.

      Program Memory~

      A 512K firmware binary size is a good "target" and rule of thumb for allowing future OTA firmware updates. Flashing over the air (OTA) requires that there is enough free program memory available to upload the new firmware along with the existing copy before the old copy is deleted. If your firmware binary is larger than the available free program memory, you can replace the existing firmware with a minimal functionality version of Tasmota (roughly 375K). This leaves enough free in the 1024K program memory for the final copy of the firmware (i.e., larger than 512K).

      Flashing Tasmota makes it simpler to update to newer versions because it is built for OTA upgrades. In fact, if the new firmware is larger than the available free memory, Tasmota's OTA process will, automatically, first replace the existing firmware with "minimal" to then have enough space to put the new firmware in.

      If one is loading firmware only via the serial interface (i.e., wired), then theoretically you could load firmware as large at the program memory size. since you can erase the flash and then fill it to the rim with the new firmware. But then you'd be left with performing upgrades by having to have physical access to the device each time.

      \ No newline at end of file diff --git a/Tutorials/index.html b/Tutorials/index.html new file mode 100644 index 0000000000..42396fe8cc --- /dev/null +++ b/Tutorials/index.html @@ -0,0 +1 @@ + Projects and Tutorials - Tasmota
      Skip to content

      Projects and Tutorials~

      Find more projects and share them in Show and Tell!

      Click on name for larger image
      TrDA's 3xINA219 (Solar+Sensor+Display) TrDA's 3xINA219 (Solar+Sensor+Display)
      TrDA's Motorised Roller Blinds TrDA's Motorised Roller Blinds
      Solar and battery power monitoring station

      Schematic
      Solar and battery power monitoring station
      Water tank monitor Water tank monitor
      Multisensor image
      Aquarium controller Aquarium controller

      Installation Tutorials~

      Video tutorials~

      3D Printed Cases~

      Don't have 3D printer? Depending on where you live, you may be able to find a third party to print the model for you. Some schools and public libraries provide printing services. Search for a printing service using 3D Hubs or send your design to a service like Shapeways.
      - Thingiverse - Yeggi

      Non-English Tutorials~

      \ No newline at end of file diff --git a/Tuya-Convert/index.html b/Tuya-Convert/index.html new file mode 100644 index 0000000000..889d4aa07e --- /dev/null +++ b/Tuya-Convert/index.html @@ -0,0 +1 @@ + Tuya Convert - Tasmota

      Tuya Convert

      Tuya devices are sold under numerous brand names but they're all identifiable by the fact that they connect with these phone apps: "Smart Life" or "Tuya Smart". They incorporate different types of Tuya Wi-Fi modules internally.

      Tuya has begun manufacturing some Wi-Fi modules using a Realtek RTL8710BN Wi-Fi SOC instead of an ESP82xx chip.
      Tasmota cannot run on Realtek devices and there are no plans on supporting them.

      Tuya-Convert is the most successful method of flashing Tuya modules without opening the device and soldering.

      Tuya-Convert comes with tasmota-lite.bin build which includes basic Tasmota features required for normal operation with Tuya devices, while removing sensors, IR, RF and home automation integration support for reduced filesize. It is recommended to upgrade to a full build of the firmware (tasmota.bin) if needed.

      To ensure Tasmota runs reliably execute the command reset 5 after Tuya-Convert is finished and Tasmota is up and running. Doing this removes fragments of the original firmware left in flash which can create issues in the future. After all that is done you can proceed with the needed configuration of your device.

      Help and troubleshooting for Tuya-Convert is done on Tuya-Convert's Github.

      Danger

      The fact that you can flash Tasmota on your device does not mean all of its features are currently supported. Please research before purchasing to see if other users have successfully flashed the device and are able to use it fully

      • Tuya-Convert video walkthrough
      • TuyOTA walkthrough
      • Mock Tuya Cloud - A general purpose framework for interacting with Tuya devices without the Tuya operated cloud.
      • Tuya API - A library for communicating with devices that use the Tuya cloud network.
      \ No newline at end of file diff --git a/Tuya-Protocols/index.html b/Tuya-Protocols/index.html new file mode 100644 index 0000000000..087596e7e7 --- /dev/null +++ b/Tuya-Protocols/index.html @@ -0,0 +1,11 @@ + + + + + + + + +Redirecting... + + diff --git a/Tuya-generic-wifi-curtain-motor-WIP/index.html b/Tuya-generic-wifi-curtain-motor-WIP/index.html new file mode 100644 index 0000000000..d8878dc4e2 --- /dev/null +++ b/Tuya-generic-wifi-curtain-motor-WIP/index.html @@ -0,0 +1 @@ + Tuya generic wifi curtain motor WIP - Tasmota
      \ No newline at end of file diff --git a/TuyaMCU-Devices/index.html b/TuyaMCU-Devices/index.html new file mode 100644 index 0000000000..49b4b13422 --- /dev/null +++ b/TuyaMCU-Devices/index.html @@ -0,0 +1 @@ + TuyaMCU Devices - Tasmota
      Skip to content

      There are several Tuya dimmer and switch variants made by various manufacturers. The switches range from 1 to 8 gangs. The dimmers are usually 1 gang. They dim mains voltage for various lighting types: incandescent, CFL, and LED. Consult the specific device for the type of bulbs and capacity it supports as well as the bulbs themselves to verify they support dimming.

      Identification and Technical details~

      The basic identification of a Tuya device is when the device information references the "Tuya Smart", "SmartLife", or "Smart Living" app. These switches and dimmers are based on a Tuya TYWE3S Wi-Fi PCB module along with an MCU. TYWE3S is based on the ESP8266 which is supported by Tasmota.

      Wiki page for TYWE3S

      The TYWE3S module mostly takes care of Wi-Fi and software features while the MCU controls the actual hardware (buttons, relays, dimmer, power measurement, etc). The MCU is interfaced to TYWE3S using the serial interface which connects to the Rx and Tx pins.

      The easiest way to identify if your switch or dimmer uses MCU is by using a continuity tester (multimeter, ohmmeter) and checking continuity from the Rx and Tx pins on TYWE3S to any other chip. Then check the datasheet of that chip to see if it is an MCU.

      Flashing - Preparation~

      To boot the TYWE3S in flashing mode, GPIO0 needs to be connected to GND while powering up. It can be left grounded for the entire process. Flashing a TYWE3S connected to a MCU is a bit trickier than one without MCU. This is due the same Rx Tx pins used by MCU and serial programmer for flashing. The TYWE3S cannot be booted to flash mode with MCU sending data over the same pins. To be able to do that, we need to disable MCU from sending data over Rx and Tx pins. There are few ways to do it:
      1. Disconnect TYWE3S module from the rest of board. (Naah, too much work) 2. Just break the Rx track from MCU to TYWE3S, flash and then reconnect. (Messy work, we want cleaner approach) 3. Just keep MCU disabled while flashing TYWE3S without any soldering / cutting. (We like that)

      The easiest is to keep MCU disabled is by identifying the NRST/RST (Reset) pin of the MCU from its datasheet and connect it to GND for the entire flashing process. This will keep MCU disabled while you flash TYWE3S. If there are some contacts or test points in switches that connect to the MCU, you might be lucky to find contacts for RST that you can easily solder onto.

      TYWE3S Connection Options~

      1. 3D print a flashing Jig and use pogo pins to make nice and easy to use flashing jig
      2. Solder wires directly onto TYWE3S
      3. Use a jumper header like the one below and bend the pins to match VCC, Rx Tx GPIO0 and GND. You can just press the jumper header to the contacts during the flashing process

      Flashing - Process~

      Once you are done identifying the pins and ready to connect, BEFORE connecting USB to PC you need to connect them as follows:
      NOTE : Use 3.3V NOT 5V

      TYWE3S Serial
      Programmer
      RX TX
      TX RX
      GPIO0 GND
      GND GND
      VCC 3.3V

      If an MCU is present, bridge RST to GND on the MCU

      Use a 6.6.0.10 Tasmota version or higher. There are lots of Tuya Serial fixes / features added in there and the tutorial below expects them.

      Now you need to follow the commands explained in the flashing tutorial.

      TIP: If you are using jumper headers use sleep 15 && before your commands, this would free your hand and give you some time to set the jumper pins and connect the USB to PC.

      Configuration~

      1. Once Tasmota is flashed on the TYWE3S, just disconnect GND -> GPIO0 (and RST if there is an MCU), and power your device again from USB.
      2. On your PC, you should see a Wi-Fi network named sonoff-xxxx where xxxx is a number from the ESP's MAC address.
      3. Connect to it and go to 192.168.4.1 in your browser. Enter the Wi-Fi credentials for your network and click save.
      4. Connect your PC back to your network. Now you need to find the IP of newly connected Tasmota device. Refer to this very good video from SuperHouseTV (ignore flashing information) about configuring Tasmota.
      5. Once you get to the Tasmota configuration you need to select TuyaMCU module assign GPIO components as indicated below depending on your hardware. You already know the pin connections to the MCU.
      GPIO Component
      01 Tuya Rx (108)
      03 Tuya Tx (107)
         
      13 Tuya Rx (108)
      15 Tuya Tx (107)
      1. If the device is connecting fine to your network, now is the time to power it down and re-assemble it. Connect a test bulb (or to the final place if you don't mind testing there)
      2. Once this is saved and device is rebooted. Open the Tasmota configuration page and you should be able to use the Toggle button to toggle the dimmer or at least one of the gangs in a multi gang switch.
      3. Follow the process here depending on switch or dimmer.

      Product Specific guides~

      Otim Dimmer~

      This Device is based on a Tuya Wi-Fi Module. Refer to "MCU Based Tuya Dimmers and Switches" for details.

      Flashing and Setup Video Guide

      These devices use a Tuya TYWE3S Wi-Fi PCB module. Once the switch is carefully popped open you will need to remove the ribbon cables for flashing and ease of soldering. An easy soldering method is to take several Dupont style jumper wires, cut one end off, and apply a bit of solder to each stripped end. This will keep the wire flexible and prevent any circuit board pads from ripping off. Apply a bit of solder to each pad necessary to flash (double check your pin-outs). Once the wire and pad have solder simply put the two together and apply a bit of heat and they will join together.

      tywe3s_3 chip_wires

      Attach the GPIO0 wire to ground during initial boot to flash. You may need to also connect MCU RST to GND during initial boot to get it into programming mode as described here. A 3-pin header bridged together works great with GPIO0, GND and the GND from the USB flasher attached. (TX pin to RX pin and RX pin to TX pin on USB flash adapter). Verify that you are using 3.3volts to flash, NOT 5V!

      Product Links:

      Costco Charging Essentials~

      This devices use a Tuya TYWE1S Wi-Fi PCB module. And it uses U1TX (GPIO15) and U1RX (GPIO13) to communicate between ESP8266 and MCU, no other GPIO is used in this device.

      Flashing~

      tywe1s CE Dimmer

      The CE dimmer uses standard Tuya GPIO

      Touch (EU and US) - Multiple manufacturers~

      Flashing~

      Tuya-Touch

      The procedure is similar to above, additionally NRST must be connected to GND during flashing.

      LedState 0 Only use the green LED for Wi-Fi/MQTT connectivity status.

      Zemismart Curtain Motor~

      Curtain motors come in a confusing array. This one has a little Wi-Fi dongle, that looks like a USB stick. But it talks using 9600 8N1, not USB. This means we can unplug the Tuya Dongle and flash it without worrying about the PCI micro. Excellent.

      Dongle and Motor

      TY-TYWE1S PCB

      U1RX and U1TX, top right of the module are connected to the USB plug on D- and D+ respectively. This dongle uses a Tuya TYWE1S, which is an ESP8266 with 2MB flash. USB3 pin R- connects the onboard LED to the MCU via a 4k7 resistor. R+, T+, and T- are all unused and unconnected on the motor PCB, so I liberated them for soldering to GPIO0, U0RX and U0TX, to flash the chip.

      modded_tuya

      • short GPIO0 and flash Tasmota
      • Connect to your Wi-Fi and get MQTT and SSL working
      • change to TuyaMCU with module 54 (will reboot)
      • Switch from U0RX/TX to U1RX/TX with backlog gpio1 0; gpio3 0; gpio15 107; gpio13 108 (will reboot)
      • Treat DpId 0x65 as a Dimmer with tuyamcu 21,101
      • Allow the dimmer to get down to 1% with setoption69 0

      And done. The curtain motor now presents as a Dimmer, with 100% full brightness = fully closed, and 0% full darkness = fully open.

      The curtain motor also presents DpId 0x66 as a single event "Full Open" 00, "Full Close" 01, and "Stop" 02 command; but as of September 2019, I can't see how to get that working.

      The curtain motor also presents DpId 0x67 as a Boolean. I have only seen value 0x01 in all my prodding. 55 aa 00 07 00 05 67 01 00 01 01 75 = 07 Status, 0005 length, 67 DpId, 01 type, 0001 length, 01 value, 75 checksum

      DM_WF_MDV4 Leading edge dimmer~

      DM_WF_MDV4 Dimmer and Case

      This is a 240V Leading Edge Dimmer with a TYWE3S controller and an STM8 MCU

      Flashing:~

      The simplest approach is to use Tuya-Convert to flash the device

      In order to flash via serial, the NRST pin of the STM8 needs to be grounded upon boot to disable it, this is brought out to a header pin, along with ground and VCC from the TYWE3S. Confirm by checking continuity with a multimeter

      IO0 from the TYWE3s also needs to be grounded upon boot, otherwise it's normal tasmota flashing procedure.

      Header J3 (STM8 debug interface) pins from left to right (Pin 1 is the square shaped) *VCC *STM8 SWIM (Pin 18) *Ground *STM8 NRST (Pin 4)

      Header

      TYWE3S

      STM8S003F3P6

      Config:~

      As per main TuyaMCU page using

      GPIO Component
      01 Tuya Rx (108)
      03 Tuya Tx (107)

      Note that the push button is wired to the MCU (PA3 Pin 10) so it cannot be used by Tasmota. Similarly the devices has a bi-color LED where one color (green) is wired to the TYWE3S (GPIO14) and the other one (red) to the MCU (PC5 Pin15).

      More information:~

      Bought from ebay

      More information on TYWE3S]

      \ No newline at end of file diff --git a/TuyaMCU/index.html b/TuyaMCU/index.html new file mode 100644 index 0000000000..f8e34cd70f --- /dev/null +++ b/TuyaMCU/index.html @@ -0,0 +1,201 @@ + TuyaMCU - Tasmota
      Skip to content

      TuyaMCU - Module (54) is configured for devices with a Tuya Wi-Fi module and a secondary MCU.

      Originally, in those devices, the Wi-Fi module takes care of network and software features. Meanwhile, the MCU controls the hardware based on commands received from the Wi-Fi module or built-in controls (buttons, switches, remotes and similar) and reports the status back to the Wi-Fi module.

      TuyaMCU module facilitates communication between Tasmota and the MCU using Tuya Serial Port Communication Protocol:

      • TuyaMCU command maps device functions to Tasmota components
      • TuyaSend<x> command calculates and sends complex serial commands using only two parameters
      • TuyaReceived MCU response interpreted and publishes as status message and a JSON payload to an MQTT topic

      TuyaMCU Command~

      Command TuyaMCU is used to map Tasmota components to Tuya device dpId's.

      Warning

      Used only if your device is defined as module TuyaMCU (54).

      Command value consists of two comma separated parameters: fnId and dpId.

      TuyaMCU <fnId>,<dpId>
      +
      where <fnId> is a Tasmota component and <dpId> is the dpId to map the function to.

      Example

      TuyaMCU 11,1 maps Relay1 (fnId 11) to dpId 1.

      If any existing entry with same fnId or dpId is already present, it will be updated to the new value.

      Entry is removed when fnId or dpId is 0.

      When no parameters are provided TuyaMCU prints the current mapped values.

      dpId~

      All the device functions controlled by the MCU are identified by a dpId.
      Whenever a command is sent to the MCU, this dpId determines which component needs to be controlled and the applies when the status is received from MCU.

      There is no way to autodetect dpId's and their functions.

      To assist in the process of determining what dpId does what, there is a bookmarklet available that can be used on the console screen. This will send the weblog and other required commands automatically, and present the TuyaMCU information in a single table allowing for easier testing.

      Use this procedure to determine which dpId's are available:

      1. Go to Configure -> Console option in Tasmota web interface.
      2. Use command weblog 4 to enable verbose logging in web interface.
      3. Observe the log. After every 9-10 seconds you should see Tuya specific messages labelled as TYA:.
      {"TuyaReceived":{"Data":"55AA0107000501010001000F","Cmnd":7,"CmndData":"0101000100","DpType1Id1":0,"1":{"DpId":1,"DpIdType":1,"DpIdData":"00"}}}
      +TYA: fnId=11 is set for dpId=1
      +TYA: RX Relay-1 --> MCU State: Off Current State:Off
      +{"TuyaReceived":{"Data":"55AA01070005020100010010","Cmnd":7,"CmndData":"0201000100","DpType1Id2":0,"2":{"DpId":2,"DpIdType":1,"DpIdData":"00"}}}
      +TYA: fnId=0 is set for dpId=2
      +{"TuyaReceived":{"Data":"55AA01070005030100010011","Cmnd":7,"CmndData":"0301000100","DpType1Id3":0,"3":{"DpId":3,"DpIdType":1,"DpIdData":"00"}}}
      +TYA: fnId=0 is set for dpId=3
      +{"TuyaReceived":{"Data":"55AA01070005040100010012","Cmnd":7,"CmndData":"0401000100","DpType1Id4":0,"4":{"DpId":4,"DpIdType":1,"DpIdData":"00"}}}
      +TYA: fnId=0 is set for dpId=4
      +
      1. Observe all lines printed as TYA: FnId=0 is set for dpId=XXX and note all dpId values.

      Now that you have a list of usable dpId's you need to determine what their functions are:

      1. Consulting our list of commonly used dpId's and existing device configurations
      2. Observing Tasmota logs while activating features of the device (with a remote or on device controls) and correlating log messages and looking at the DpIdType and DpIdData values (eg: boolean vs value)
      3. Extrapolating possible function of the dpId based on Data Type and Function Command, then testing using TuyaSend<x>

      fnId~

      Identifier used in TuyaMCU command to map a dpId to a Tasmota component.

      Component FnId Note
      Switch1 to Switch4 1 to 4 Map only to dpId with on / off function
      Relay1 to Relay8 11 to 18 Map only to dpId with on / off function
      Lights 21 to 28 21 for Dimmer
      22 for Dimmer2
      23 for CCT Light
      24 for RGB light
      25 for white light
      26 for light mode set (0 = white and 1 = color)
      27 to report the state of Dimmer1
      28 to report the state of Dimmer2
      Power Monitoring 31 to 33 31 for Power (in deci Watt)
      32 for Current (in milli Amps)
      33 for Voltage (in deci Volt)
      Relay1i to Relay8i 41 to 48 Map only to dpId with on / off function
      Battery powered sensor mode 51 Battery powered devices use a slightly different protocol
      Enum dpId 61 to 64 Range for each enum is 0 to 31
      Sensors 71 to 79 Range of sensors (temperature, humidity, co2, gas, etc)
      Timers 81 to 84 Manage integer based timers
      Extra functions 97 to 99 97 for motor direction
      98 for error logging (report only)
      99 as a dummy function

      Note

      This component is under active development which means the function list may expand in the future.

      Since the majority of devices have a power on/off functions on dpId 1 it's mapped to fnId 11 (Relay1) by default. If you don't need it, map it to fnId 99 with TuyaMcu 99,1

      Danger

      Mapping a relay or switch to a dpId that is not a simple on/off function (data Type 1) might result in unwanted power toggling (i.e. dpId sends value of 4 which toggles the relay to Power 4 aka blink mode)

      TuyaSend Command~

      Command TuyaSend is used to send commands to dpId's. It is required for dpId's that shouldn't be mapped to a fnId.

      With this command it is possible to control every function of the dpId that is controllable, providing you know its data type and data length. With them provided, the rest of the protocol command is calculated.

      Command's value consists of two comma separated parameters: dpId and data.

      TuyaSend<x> dpId,data

      TuyaSend0~

      Used without payload to query states of dpID's.

      TuyaSend1~

      Sends boolean (Type 1) data (0/1) to dpId (Max data length 1 byte)

      Example

      TuyaSend1 1,0 sends value 0 to dpId=1 switching the device off

      TuyaSend2~

      Sends integer or 4 byte (Type 2) data to dpId (Max data length 4 bytes)

      Example

      TuyaSend2 14,100 sends value 100 to dpId=14 setting timer to 100 minutes

      TuyaSend3~

      Sends an ASCII string (Type 3) data to dpId (Max data length? Not known at this time).

      Warning

      Note that when sending color values, the MCU may interpret lower case and upper case hex codes differently. You may need to test with your specific MCU to ensure that the values sent properly render the color you desire.

      Example

      TuyaSend3 108,ff0000646464ff sends a 14 char hex string to dpId=108 (Type 3) containing RGBHSV values to control a light

      TuyaSend4~

      Sends enum (Type 4) data (0/1/2/3/4/5) to dpId (Max data length 1 bytes)

      Example

      TuyaSend4 103,2 sends value 2 to dpId=103 to set fan speed to high

      TuyaSend5~

      Sends an HEX string (Type 3) data to dpId (Max data length? Not known at this time). Does NOT require 0x prefix.

      Example

      TuyaSend5 108, ABCD sends a string of 2 bytes defined by hex codes to dpId=108

      Differences between TuyaSend3 and TuyaSend5:

      • TuyaSend3 108, ABCD sends 55aa000600086c030004414243448a where the payload is 41424344 which is the ASCII bytes representing the string ABCD
      • TuyaSend5 108, ABCD sends 55aa000600066c030002abcdf4 where the payload is a abcd which is a string of 2 bytes 0xAB and 0xCD

      TuyaSend6~

      Sends payload with raw data (type 0) to dpId (Max data length? Not known at this time). Does NOT require 0x prefix.

      Example

      TuyaSend6 37, 060000DC08000096 sends raw data defined by hex codes to dpId=37

      Differences between TuyaSend3, TuyaSend5 and TuyaSend6: * TuyaSend3 and TuyaSend5 sends payload with the data type 0x03 = string * TuyaSend6 sends payload with the data type 0x00 = raw

      TuyaSend8~

      Used without payload to get device information and dpId states. Replaces SerialSend5 55aa000100000

      TuyaSend9~

      Use without any payload to toggle a new STAT topic reporting changes to a dpId, for example:

      17:45:38 MQT: stat/TuyaMCU/DPTYPE1ID1 = 1
      +

      TuyaReceived~

      Every status message from the MCU gets a JSON response named TuyaReceived which contains the MCU protocol status message inside key/value pairs which are hidden from the user by default.

      To publish them to a MQTT Topic of tele/%topic%/RESULT you need to enable SetOption66 1.

      To avoid some cyclic MCU <-> ESP messages published via MQTT to the topic tele/%topic%/RESULT you need to enable SetOption137 1.

      If SetOption137 set to 1 the following cmds will not forwarded: - the heartbeat between the MCU <-> ESP (every 10 seconds, TUYA_CMD_HEARTBEAT) - the Wifi-State between the MCU <-> ESP (during start-up and wifi events, TUYA_CMD_WIFI_STATE) - the local time info query of the MCU (every minute, TUYA_CMD_SET_TIME) - the received update package info from MCU (during firmware update of Tuya MCU, TUYA_CMD_UPGRADE_PACKAGE)

      If SetOption137 set to 0 all received Tuya MCU messages will be published. SetOption137 is very useful to reduce the MQTT-traffic.

      Example~

      After issuing serial command 55aa0006000501010001010e (Device power (dpId=1) is mapped to Relay1 (fnId=11)) we get the following console output (with weblog 4):

      19:54:18 TYA: Send "55aa0006000501010001010e"
      +19:54:18 MQT: stat/GD-30W/STATE = {"Time":"2019-10-25T19:54:18","Uptime":"0T01:45:51","UptimeSec":6351,"Heap":27,"SleepMode":"Dynamic","Sleep":0,"LoadAvg":999,"MqttCount":1,"POWER1":"ON","POWER2":"OFF","POWER3":"ON","POWER4":"OFF","POWER5":"ON","Dimmer":100,"Fade":"OFF","Speed":1,"LedTable":"OFF","Wifi":{"AP":1,"SSId":"HTPC","BSSId":"50:64:2B:5B:41:59","Channel":10,"RSSI":24,"LinkCount":1,"Downtime":"0T00:00:08"}}
      +19:54:18 MQT: stat/GD-30W/RESULT = {"POWER1":"ON"}
      +19:54:18 MQT: stat/GD-30W/POWER1 = ON
      +19:54:18 MQT: stat/GD-30W/RESULT = {"TuyaReceived":{"Data":"55AA0007000501010001010F","ChkSum":"0x0F","Cmnd":7,"CmndDataLen":5,"CmndData":"0101000101","DpId":1,"DpIdType":1,"DpIdLen":1,"DpIdData":"01"}}
      +19:54:18 TYA: fnId=11 is set for dpId=1
      +19:54:18 TYA: RX Relay-1 --> MCU State: On Current State:On
      +
      Above TYA: fnId=11 is set for dpId=1 you can see the JSON response for that dpId. This JSON string displays the response MCU gave to our command.

      "Data" field contains the complete response and the rest of the key/value pairs show the protocol broken into parts. "DpId", "DpIdData" and "DpIdType" are the ones we're most interested in since we can use them for TuyaSend.

      Tip

      Use command TuyaSend8 and/or TuyaSend0 at any time to request statuses of all dpId's from the MCU.

      Use in Rules~

      This data can also be used as a trigger for rules using TuyaReceivedData#Data=hex_string

      Rule1 on TuyaReceived#Data=55AA000700056E040001007E do publish2 stat/tuya_light/effect rgb_cycle endon
      +
      will publish a status message to a custom topic when 55AA000700056E040001007E appears in "Data" field of the response.

      Device Configurations~

      Before proceeding identify dpId's and their function.

      Dimmer~

      We need to configure four functions of a dimmer:

      1. Dimming dpId
      2. Dimming Range
      3. Dimming less than 10%

      Dimming dpId~

      The dimmer FunctionId is 21. On a dimmer dpId generally is 2 or 3. Try both.

      1. Go to the Tasmota Console and type TuyaMCU 21,2 and wait for it to reboot.
      2. Enter Backlog Dimmer 10; Dimmer 100 in the Console.
      3. If your bulb responds to Dimmer commands, you have successfully configured the dimmer FunctionId. Make note of it.
      4. If not try id 3 and if even 3 doesn't work keep trying Ids from all unknown Ids from the log until one works.

      Dimming Range~

      Once you have figured out the dimming functionId, we need to find the maximum dimming range. Once the dimming Id is set, the logs will continue

      TYA: Heartbeat  
      +TYA: RX Packet: "55aa03070005010100010011"  
      +TYA: RX Relay-1 --> MCU State: Off Current State:Off    
      +TYA: RX Packet: "55aa03070008020200040000000720"    
      +TYA: FnId=21 is set for dpId=2  
      +TYA: RX Dim State=7 
      +

      Now using the hardware buttons increase the dimmer to its maximum and observe the log. The Dim State=XXX shows the current dimmer level reported by MCU. Once the dimmer is at max, note this number. Again using hardware buttons decrease dimmer to minimum and note the number for minimum.
      Now we need to tell Tasmota to use maximum and minimum values. This controlled by DimmerRange command. We can set it using DimmerRange <Min>,<Max> where <Min> is the minimum dimmer state and <Max> maximum dimmer state reported in logs.

      Once set, try dimmer 100 in the Console and check if the brightness of bulb is same is the same as when the maximum was set using hardware buttons.

      Warning

      Some Tuya devices automatically send the state of a dimmer after a power off. Tasmota could misunderstand the command and try to turn on the light even with SetOption20 and SetOption54 enabled.

      Dual Dimmer~

      To enable a dual dimmer setup assign fnId's:

      • 21 as Dimmer1
      • 22 as Dimmer2
      • 11 as Relay1
      • 12 as Relay2

      Tasmota will automatically enable SetOption68 and the dimmers will respond to Channel1 and Channel2 commands.

      Warning

      The use of SetOption68 is limited to two channels and will be automatically disabled if any other combination of lights is used.

      Lights~

      CCT Light~

      To enable a CCT light assign fnId's:

      • 21 as Dimmer1
      • 11 as Relay1
      • 23 as CT channel

      RGB Light~

      To enable an RGB light assign fnId's:

      • 21 as Dimmer1
      • 11 as Relay1
      • 24 as RGB controller

      TuyaMCU uses different types of RGB Hex format where the most recent is 0HUE0SAT0BRI0 (type 1) and the older being RRGGBBFFFF6464 (type 2). Depending on the MCU, code can be case sensitive.

      After enabling the RGB function check the TuyaReceived information and use TuyaRGB to configure and store the correct (or the closest) format:

      • TuyaRGB 0 - Type 1, 12 characters uppercase. Example: 00DF00DC0244 (default)
      • TuyaRGB 1 - Type 1, 12 characters lowercase. Example: 008003e8037a
      • TuyaRGB 2 - Type 2, 14 characters uppercase. Example: 00FF00FFFF6464
      • TuyaRGB 3 - Type 2, 14 characters lowercase. Example: 00e420ffff6464

      TuyaRGB without payload will return the actual configured format.

      RGB+X Light~

      To enable an RGB+W light use RGB Light configuration and assign fnId 25 as white color.

      To enable an RGB+CCT light use RGB Light configuration and assign fnId 23 as CT channel.

      Light mode selector~

      The majority of  TuyaMCU devices with an RGB+W or an RGB+CCT light have a button or app function to switch between White and Colored light.

      To do the same in Tasmota, assign function (fnId) 26 to the mode select dpId. The possible values are 0 (white) and 1 (colorful). A button on the WebUI will be available once configured.

      When the ModeSet function is enabled it is not possible to update both lights at the same time. Only the currently selected light mode will be updated.

      Warning

      Use of SetOption68 for more than two channels and the light split option (SetOption37 >= 128) are not supported in TuyaMCU mode.

      Enums~

      Better control over Type4 or enum dpId's. Up to four can be added, with a range from 0 to 31.

      • 61 as Enum1
      • 62 as Enum2
      • 63 as Enum3
      • 64 as Enum4

      After an enum is configured, use TuyaEnumList to declare the range it must respect (note 0 is always the first item in range).

      TuyaEnumlist <enum>,<range> where <enum> is declared using TuyaMCU and <range> is 0..31.

      Example: configure Enum 1 with a range from 0 to 8.

      21:14:52 CMD: tuyaenumlist 1,8
      +21:14:52 MQT: stat/TuyaMCU/RESULT = {"TuyaEnumList":{"Enum1":8,"Enum2":9,"Enum4":1}}
      +

      Entering a value greater than 31 will return an error

      TuyaEnumList without payload will return the configuration of all the enums enabled in the list.

      To update an enum use the command TuyaEnum:

      Usage TuyaEnum [1|2|3|4],[TuyaEnumList range]

      Example: update Enum 2 to 4.

      21:14:12 CMD: tuyaenum2 4
      +21:14:12 MQT: stat/TuyaMCU/RESULT = {"TuyaEnum2":4}
      +

      Entering a value not in range will return an error

      TuyaEnum without payload will return the state of all the enums configured.

      Sensors~

      These are the currently available sensors:

      • 71 as Temperature Sensor
      • 73 as Humidity Sensor
      • 75 as Illuminance Sensor
      • 76 as TVOC Sensor
      • 77 as CO2 Sensor
      • 78 as ECO2 Sensor
      • 79 as %LEL gas Sensor

      If your device has a dpId for setting a specific Temperature and/or Humidity:

      • 72 for Temperature Set
      • 74 for Humidity Set

      Use TuyaSend2 to manage them.

      Temperature and Temperature Set default to °C. If you need °F change SetOption8 to 1.

      The TuyaMCU driver sends the temperature as a byte integer. As of 9.3.x, the integer is converted by Tasmota to a float based on the "TempRes" setting which indicates the number of places after the decimal. The TempRes setting is by default "1" which means a device which sends 101 will be interpreted as 10.1. If your device normally returns an integer temperature, you may need to set TempRes to "0".

      If your device requires the temperature to be divided (eg. increases in .5° increments), you may need to use the rules functionality to convert the temperature value.

      Please note this will not update the value sent by the MCU but will just change the unit of measure reported on /SENSOR topic. You have to find a dpid to set the correct unit and change reported values (if it exists) or perhaps use the rules functionality to do the conversion.

      Timers~

      4 Type2 (integer) timers can be managed directly from Tasmota

      • 81 as Timer1
      • 82 as Timer2
      • 83 as Timer3
      • 84 as Timer4

      Timers can be managed with TuyaSend2 and are visible in the WebUI.

      Covers~

      Single shutter or double shutters devices can be managed with a dimmer setup For devices that are reporting position to another dpId assign fnId's:

      • 27 to report the state of Dimmer1
      • 28 to report the state of Dimmer2

      If your cover device has a motor direction change option assign fnId 97 for motor direction.

      Switches~

      There is currently no way to detect the number of relays present in an MCU based switch. We need to tell the number of relays to Tasmota using FunctionIDs 12 to 18 for Relay2 to Relay4.

      Example

      For a 4 gang switch set TuyaMCU 12,2, TuyaMCU 13,3 and TuyaMCU 14,4 if the dpIds for Relays 2-4 are 2,3,4.

      Tip

      You can configure all at once by using Backlog TuyaMCU 12,2; TuyaMCU 13,3; TuyaMCU 14,4

      Power Metering~

      Some Tuya MCU devices support Power measurement support over serial. For this it is better to use a bulb with known wattage rating.

      Observe the logs in the Console

      TYA: Heartbeat
      +{"TuyaReceived":{"Data":"55AA0107000501010001000F","Cmnd":7,"CmndData":"0101000100","DpType1Id1":0,"1":{"DpId":1,"DpIdType":1,"DpIdData":"00"}}}
      +TYA: fnId=11 is set for dpId=1
      +TYA: RX Relay-1 --> MCU State: Off Current State:Off
      +{"TuyaReceived":{"Data":"55AA01070005020100010111","Cmnd":7,"CmndData":"0201000101","DpType1Id2":1,"2":{"DpId":2,"DpIdType":1,"DpIdData":"01"}}}
      +TYA: fnId=0 is set for dpId=2
      +{"TuyaReceived":{"Data":"55AA01070005030100010011","Cmnd":7,"CmndData":"0301000100","DpType1Id3":0,"3":{"DpId":3,"DpIdType":1,"DpIdData":"00"}}}
      +TYA: fnId=0 is set for dpId=3
      +{"TuyaReceived":{"Data":"55AA01070005040100010113","Cmnd":7,"CmndData":"0401000101","DpType1Id4":1,"4":{"DpId":4,"DpIdType":1,"DpIdData":"01"}}}
      +TYA: fnId=0 is set for dpId=4
      +{"TuyaReceived":{"Data":"55AA0107000807020004000000001C","Cmnd":7,"CmndData":"0702000400000000","DpType2Id7":0,"7":{"DpId":7,"DpIdType":2,"DpIdData":"00000000"}}}
      +TYA: fnId=0 is set for dpId=7
      +{"TuyaReceived":{"Data":"55AA0107000808020004000000001D","Cmnd":7,"CmndData":"0802000400000000","DpType2Id8":0,"8":{"DpId":8,"DpIdType":2,"DpIdData":"00000000"}}}
      +TYA: fnId=0 is set for dpId=8
      +{"TuyaReceived":{"Data":"55AA0107000809020004000000001E","Cmnd":7,"CmndData":"0902000400000000","DpType2Id9":0,"9":{"DpId":9,"DpIdType":2,"DpIdData":"00000000"}}}
      +TYA: fnId=0 is set for dpId=9
      +{"TuyaReceived":{"Data":"55AA010700080A020004000000001F","Cmnd":7,"CmndData":"0A02000400000000","DpType2Id10":0,"10":{"DpId":10,"DpIdType":2,"DpIdData":"00000000"}}}
      +TYA: fnId=0 is set for dpId=10
      +{"TuyaReceived":{"Data":"55AA0107000865020004000000007A","Cmnd":7,"CmndData":"6502000400000000","DpType2Id101":0,"101":{"DpId":101,"DpIdType":2,"DpIdData":"00000000"}}}
      +TYA: fnId=0 is set for dpId=101
      +{"TuyaReceived":{"Data":"55AA01070008660200040000009813","Cmnd":7,"CmndData":"6602000400000098","DpType2Id102":152,"102":{"DpId":102,"DpIdType":2,"DpIdData":"00000098"}}}
      +TYA: fnId=0 is set for dpId=102
      +{"TuyaReceived":{"Data":"55AA01070008670200040000017EFB","Cmnd":7,"CmndData":"670200040000017E","DpType2Id103":382,"103":{"DpId":103,"DpIdType":2,"DpIdData":"0000017E"}}}
      +TYA: fnId=0 is set for dpId=103
      +{"TuyaReceived":{"Data":"55AA0107000868020004000009951B","Cmnd":7,"CmndData":"6802000400000995","DpType2Id104":2453,"104":{"DpId":104,"DpIdType":2,"DpIdData":"00000995"}}}
      +TYA: fnId=0 is set for dpId=104
      +

      In the TuyaReceived we are interested in DpIdData. For example: 00000995 is the second last entry.

      • Make sure the bulb if off.
      • Find out the voltage standard of your country (generally 220, 240, 120v) from this table.
      • Multiply that number by 10 (2400) and Convert that number (2400) to Hex using any hex converter (2400 = 0x960)
      • Now look for the number nearest to 960 in the logs. In our case it is 00000995. So we expect that's the voltage which is "DpId":104 in our example.
      • Set voltage functionId 33 by entering TuyaMCU 33,104.
      • Now set dimmer to 100% using the dimmer 100 command, or power on using power1 on (depending on the device) and observe the logs.
      • Now we need the power rating of your bulb, for example 40W. Multiply by 10 (400) and convert to hex which gives us 0x190. Check which unknown ID is close to 190. I this example it is 17E for "DpId":103. This is the Id of Active Power function.
      • Set the active power functionId 31 by entering TuyaMCU 31,103.
      • Once Power and Voltage are set you should see something such as this in the logs:
        TYA: Heartbeat
        +{"TuyaReceived":{"Data":"55AA0107000501010001000F","Cmnd":7,"CmndData":"0101000100","DpType1Id1":0,"1":{"DpId":1,"DpIdType":1,"DpIdData":"00"}}}
        +TYA: fnId=11 is set for dpId=1
        +TYA: RX Relay-1 --> MCU State: Off Current State:Off
        +{"TuyaReceived":{"Data":"55AA01070005020100010111","Cmnd":7,"CmndData":"0201000101","DpType1Id2":1,"2":{"DpId":2,"DpIdType":1,"DpIdData":"01"}}}
        +TYA: fnId=0 is set for dpId=2
        +{"TuyaReceived":{"Data":"55AA01070005030100010011","Cmnd":7,"CmndData":"0301000100","DpType1Id3":0,"3":{"DpId":3,"DpIdType":1,"DpIdData":"00"}}}
        +TYA: fnId=0 is set for dpId=3
        +{"TuyaReceived":{"Data":"55AA01070005040100010113","Cmnd":7,"CmndData":"0401000101","DpType1Id4":1,"4":{"DpId":4,"DpIdType":1,"DpIdData":"01"}}}
        +TYA: fnId=0 is set for dpId=4
        +{"TuyaReceived":{"Data":"55AA0107000807020004000000001C","Cmnd":7,"CmndData":"0702000400000000","DpType2Id7":0,"7":{"DpId":7,"DpIdType":2,"DpIdData":"00000000"}}}
        +TYA: fnId=0 is set for dpId=7
        +{"TuyaReceived":{"Data":"55AA0107000808020004000000001D","Cmnd":7,"CmndData":"0802000400000000","DpType2Id8":0,"8":{"DpId":8,"DpIdType":2,"DpIdData":"00000000"}}}
        +TYA: fnId=0 is set for dpId=8
        +{"TuyaReceived":{"Data":"55AA0107000809020004000000001E","Cmnd":7,"CmndData":"0902000400000000","DpType2Id9":0,"9":{"DpId":9,"DpIdType":2,"DpIdData":"00000000"}}}
        +TYA: fnId=0 is set for dpId=9
        +{"TuyaReceived":{"Data":"55AA010700080A020004000000001F","Cmnd":7,"CmndData":"0A02000400000000","DpType2Id10":0,"10":{"DpId":10,"DpIdType":2,"DpIdData":"00000000"}}}
        +TYA: fnId=0 is set for dpId=10
        +{"TuyaReceived":{"Data":"55AA0107000865020004000000007A","Cmnd":7,"CmndData":"6502000400000000","DpType2Id101":0,"101":{"DpId":101,"DpIdType":2,"DpIdData":"00000000"}}}
        +TYA: fnId=0 is set for dpId=101
        +{"TuyaReceived":{"Data":"55AA01070008660200040000009712","Cmnd":7,"CmndData":"6602000400000097","DpType2Id102":151,"102":{"DpId":102,"DpIdType":2,"DpIdData":"00000097"}}}
        +TYA: fnId=0 is set for dpId=102
        +{"TuyaReceived":{"Data":"55AA01070008670200040000017BF8","Cmnd":7,"CmndData":"670200040000017B","DpType2Id103":379,"103":{"DpId":103,"DpIdType":2,"DpIdData":"0000017B"}}}
        +TYA: fnId=31 is set for dpId=103
        +TYA: Rx ID=103 Active_Power=379
        +{"TuyaReceived":{"Data":"55AA0107000868020004000009961C","Cmnd":7,"CmndData":"6802000400000996","DpType2Id104":2454,"104":{"DpId":104,"DpIdType":2,"DpIdData":"00000996"}}}
        +TYA: fnId=33 is set for dpId=104
        +TYA: Rx ID=104 Voltage=2454
        +
      • To get the Id for the current, calculate Current = Power / Voltage ( 37.9 / 245.4 ) = ~0.1544 (Remember to divide each value by 10). Multiply this by 1000 = 154. Now convert 154 to hex which is 0x9A. This is closest to 97 which is Id "DpId":102.
      • Set the current FunctionId 32 using command TuyaMCU 32,102.
      • Observe the logs should start showing Current in addition to Active_Power and Voltage
        TYA: Heartbeat
        +{"TuyaReceived":{"Data":"55AA0107000501010001000F","Cmnd":7,"CmndData":"0101000100","DpType1Id1":0,"1":{"DpId":1,"DpIdType":1,"DpIdData":"00"}}}
        +TYA: fnId=11 is set for dpId=1
        +TYA: RX Relay-1 --> MCU State: Off Current State:Off
        +{"TuyaReceived":{"Data":"55AA01070005020100010111","Cmnd":7,"CmndData":"0201000101","DpType1Id2":1,"2":{"DpId":2,"DpIdType":1,"DpIdData":"01"}}}
        +TYA: fnId=0 is set for dpId=2
        +{"TuyaReceived":{"Data":"55AA01070005030100010011","Cmnd":7,"CmndData":"0301000100","DpType1Id3":0,"3":{"DpId":3,"DpIdType":1,"DpIdData":"00"}}}
        +TYA: fnId=0 is set for dpId=3
        +{"TuyaReceived":{"Data":"55AA01070005040100010113","Cmnd":7,"CmndData":"0401000101","DpType1Id4":1,"4":{"DpId":4,"DpIdType":1,"DpIdData":"01"}}}
        +TYA: fnId=0 is set for dpId=4
        +{"TuyaReceived":{"Data":"55AA0107000807020004000000001C","Cmnd":7,"CmndData":"0702000400000000","DpType2Id7":0,"7":{"DpId":7,"DpIdType":2,"DpIdData":"00000000"}}}
        +TYA: fnId=0 is set for dpId=7
        +{"TuyaReceived":{"Data":"55AA0107000808020004000000001D","Cmnd":7,"CmndData":"0802000400000000","DpType2Id8":0,"8":{"DpId":8,"DpIdType":2,"DpIdData":"00000000"}}}
        +TYA: fnId=0 is set for dpId=8
        +{"TuyaReceived":{"Data":"55AA0107000809020004000000001E","Cmnd":7,"CmndData":"0902000400000000","DpType2Id9":0,"9":{"DpId":9,"DpIdType":2,"DpIdData":"00000000"}}}
        +TYA: fnId=0 is set for dpId=9
        +{"TuyaReceived":{"Data":"55AA010700080A020004000000001F","Cmnd":7,"CmndData":"0A02000400000000","DpType2Id10":0,"10":{"DpId":10,"DpIdType":2,"DpIdData":"00000000"}}}
        +TYA: fnId=0 is set for dpId=10
        +{"TuyaReceived":{"Data":"55AA0107000865020004000000007A","Cmnd":7,"CmndData":"6502000400000000","DpType2Id101":0,"101":{"DpId":101,"DpIdType":2,"DpIdData":"00000000"}}}
        +TYA: fnId=0 is set for dpId=101
        +{"TuyaReceived":{"Data":"55AA01070008660200040000009712","Cmnd":7,"CmndData":"6602000400000097","DpType2Id102":151,"102":{"DpId":102,"DpIdType":2,"DpIdData":"00000097"}}}
        +TYA: fnId=32 is set for dpId=102
        +TYA: Rx ID=102 Current=151
        +{"TuyaReceived":{"Data":"55AA01070008670200040000017BF8","Cmnd":7,"CmndData":"670200040000017B","DpType2Id103":379,"103":{"DpId":103,"DpIdType":2,"DpIdData":"0000017B"}}}
        +TYA: fnId=31 is set for dpId=103
        +TYA: Rx ID=103 Active_Power=379
        +{"TuyaReceived":{"Data":"55AA0107000868020004000009961C","Cmnd":7,"CmndData":"6802000400000996","DpType2Id104":2454,"104":{"DpId":104,"DpIdType":2,"DpIdData":"00000996"}}}
        +TYA: fnId=33 is set for dpId=104
        +TYA: Rx ID=104 Voltage=2454
        +
      • Power and current should change on dimming high / low or turning the device on and off. The Tasmota web UI should show power values now on the home page.

      Battery Powered Sensors~

      Specific Devices~

      Tuya Protocols~

      The MCU communicates with the Wi-Fi module through the serial port with a Tuya specified protocol. Those are classified into basic and functional protocols.

      Basic protocols~

      They are common protocols integrated in Tasmota's TuyaMCU module. They stay the same for each product and are mandatory for Tuya module to work correctly.

      Functional protocols~

      Functional protocols are used for delivering and reporting data of functions. These protocols differ between devices and manufacturers and might require configuration in Tasmota using TuyaMCU command or with TuyaSend<x> command.

      Anatomy of Tuya Protocol~

      Name Description
      Frame Header Version Fixed value of 0x55aa
      Command Word 0x06 - send commands
      0x07 - report status
      Data Length defines expected length of data
      dpID numbered ID of a function (DP = Data Point or Define Product)
      Data Type see Data Type table below
      Function Length length of command
      Function Command formatted according to Data Type
      Verification Method checksum = remainder of the byte sum starting from Frame Header to 256

      Data Type~

      Hex Tasmota Command Description Max length
      0x00 TuyaSend6 raw data unknown
      0x01 TuyaSend1 boolean data 0/1 1 byte
      0x02 TuyaSend2 value data. If a value contains less than 4 bytes, 0 is supplemented before 4 bytes
      0x00 TuyaSend3 string data unknown
      0x04 TuyaSend4 enum data 0/1/2/3/4/5 1 byte
      0x05 ### fault data, report only 8 bytes

      Let's dissect and explain the MCU protocol using serial command 55aa0006000501010001010e:

      Frame Header Version Command Word Data Length dpID Data Type Function Length Function Command Verification Method
      55aa00 06 0005 01 01 0001 01 0e

      This is the command which powers on the device sending Function Command = 1 to dpID 1 (Switch):

      • Frame Header Version = 0x55aa00 which is a fixed value and always the same
      • Command Word = 0x06 because we're sending a command
      • Data Type = 0x01 since the command sent is a 1 byte boolean
      • Function Length = 0x001 instruct 1 character only for function command length
      • Function Command = 0x01 in hex which equals 1 in int
      • Verification Method = 0e is calculated

      Protocol flow~

      On device boot, TuyaMCU executes the required basic protocols and reads the functional protocol data received, which are used to update status of components mapped in TuyaMCU (Relays, dimmer, power monitoring data).

      After receiving a command from Tasmota (Command Word 0x06), the MCU performs corresponding logical control. When the dpID status is changed, the MCU reports the data (Command Word 0x07) to TuyaMCU component.

      dpId Function Tables~

      This information is just for orientation. Functions are assigned by the manufacturer and can be on different dpId's

      • DP ID: dpId.
      • Function Point:Used to describe the product function.
      • Identifier: Function codename. Can only be letters, numbers and underscores
      • Data type:
      • Issue and report: command data can be sent and status data can be reported back to the Wi-Fi module
      • Report only: supports only status reporting, no control options

      • Function Type (Referred as Data Type):

      • Boolean (bool): non-true or false binary variable, such as: switch function, on / off
      • Value (value): suitable for linear adjustment of the type of data, such as: temperature regulation, temperature range 20-40 ℃
      • Enum (enum): custom finite set value, such as: working levels, low / mid / high
      • Fault (fault): dedicated to reporting and statistical failure of the function points. Support multi-fault, the data is reported only
      • Integer(integer): transmitted as integer
      • Transparent (raw): data in binary

      Switches or Plugs/Power Strips~

      DP ID Identifier Data type Function type Properties
      1 switch_1 Control and report Boolean
      2 switch_2 Control and report Boolean
      3 switch_3 Control and report Boolean
      4 switch_4 Control and report Boolean
      5 switch_5 Control and report Boolean
      9 countdown_1 Control and report Integer undefined0-86400, undefined1, Scale0, Unit:s
      10 countdown_2 Control and report Integer undefined0-86400, undefined1, Scale0, Unit:s
      11 countdown_3 Control and report Integer undefined0-86400, undefined1, Scale0, Unit:s
      12 countdown_4 Control and report Integer undefined0-86400, undefined1, Scale0, Unit:s
      13 countdown_5 Control and report Integer undefined0-86400, undefined1, Scale0, Unit:s

      Aromatherapy Machine (Oil Diffuser)~

      DP ID Function points Identifier Data type Function type Properties
      1 Switch Power Issue and report Boolean
      6 Amount of fog fog Issue and report Enum Enumerated values:small, large
      11 Light Light Issue and report Boolean
      12 Fault alarm fault Only report Fault Barrier values:1
      13 Countdown countdown Issue and report Enum Enumerated values: 0, 1, 2, 3
      14 Countdown remaining time countdown_left Only report Integer Values range: 0-360, Pitch1, Scale0, Unit:min
      101 Light mode work_mode Issue and report Enum Enumerated values: white, colour, scene, scene1, scene2, scene3, scene4
      102 Color value colour_data Issue and report Char type *see below
      103 Light mode lightmode Issue and report Enum Enumerated values: 1, 2, 3
      104 Brightness setting setlight Issue and report Integer Values range: 0-255, Pitch1, Scale0, Unit:\%

      colour_data format of the lights is a string of 14 characters, for example, 00112233334455, where 00 indicates R, 11 indicates G, 22 indicates B, 3333 indicates the hue, 44 indicates the saturation, and 55 indicates the value. The initial value is saved by default. If you do not want to adjust the light, set the data to the maximum value 100% (0x64). The last four characters have fixed values.

      Curtain Motor~

      DP ID Function points Identifier Data type Function type Properties
      1 Percentage percent_state Only report Integer Values range:0-100, Pitch1, Scale0, Unit:%
      2 Motor Direction control_back Issue and report Boolean
      3 Auto Power auto_power Issue and report Boolean
      4 Left time countdown Issue and report Enum Enumerated values:cancel, 1, 2, 3, 4
      5 Total Time time_total Only report Integer Values range:0-120000, Pitch1, Scale0, Unit:m

      Complete document on protocols

      DP ID Function points Identifier Data type Function type Properties
      1 Control (required) control Issue and report Enum Enumerated values:open, stop, close, continue
      2 Curtain position setting percent_control Issue and report Integer Values range:0-100, Pitch1, Scale0, Unit:%
      3 Current curtain position percent_state Only report Integer Values range:0-100, Pitch1, Scale0, Unit:%
      4 Mode mode Issue and report Enum Enumerated values:morning, night
      5 Motor Direction control_back Issue and report Enum Enumerated values:forward, back
      6 Auto Power auto_power Issue and report Boolean
      7 Work State (required) work_state Only report Enum Enumerated values:opening, closing
      11 Situation_set situation_set Only report Enum Enumerated values:fully_open, fully_close
      12 Fault (required) fault Only report Fault Barrier values:motor_fault

      Power Monitoring Plug~

      DP ID Function points Identifier Data type Function type Properties
      1 switch_1 switch_1 Control and report Boolean
      9 countdown_1 countdown_1 Control and report Integer undefined0-86400, undefined1, Scale0, Unit:s
      17 statistics Function add_ele Control and report Integer undefined0-50000, undefined100, Scale3, Unit:
      18 current cur_current Data report Integer undefined0-30000, undefined1, Scale0, Unit:mA
      19 power cur_power Data report Integer undefined0-50000, undefined1, Scale1, Unit:W
      20 voltage cur_voltage Data report Integer undefined0-5000, undefined1, Scale1, Unit:V
      21 test flag test_bit Data report Integer undefined0-5, undefined1, Scale0, Unit:
      22 voltage coefficient voltage_coe Data report Integer undefined0-1000000, undefined1, Scale0, Unit:
      23 current coefficient electric_coe Data report Integer undefined0-1000000, undefined1, Scale0, Unit:
      24 power coefficient power_coe Data report Integer undefined0-1000000, undefined1, Scale0, Unit:
      25 statistics coefficient electricity_coe Data report Integer undefined0-1000000, undefined1, Scale0, Unit:
      26 warning fault Data report Fault Barrier values:ov_cr

      Dehumidifier~

      DP ID Function points Identifier Data type Function type Properties
      1 Switch Switch Issue and report Boolean
      2 PM2.5 PM25 Only report Integer Values range:0-999, Pitch1, Scale0, Unit:
      3 Work mode Mode Issue and report Enum Enumerated values:Manual, Auto, Sleep
      4 Wind speed Speed Issue and report Enum Enumerated values:speed1, speed2, speed3, speed4, speed5
      5 Filter usage Filter Only report Integer Values range:0-100, Pitch1, Scale0, Unit:%
      6 Fresh Anion Issue and report Boolean
      7 Child lock Lock Issue and report Boolean
      9 UV light UV Issue and report Boolean
      11 Filter reset FilterReset Issue and report Boolean
      12 indoor temp Temp Only report Integer Values range:-20-50, Pitch1, Scale0, Unit:℃
      13 Indoor humidity Humidity Only report Integer Values range:0-100, Pitch1, Scale0, Unit:%

      Lighting~

      DP ID Function points Identifier Data type Function type Properties
      1 Switch led_switch Control and report Boolean
      2 Mode work_mode Control and report Enum Enumerated values:white, colour, scene, scene_1, scene_2, scene_3, scene_4
      3 Bright bright_value Control and report Integer undefined25-255, undefined1, Scale0, Unit:
      5 Colour mode colour_data Control and report Char type
      6 Scene scene_data Control and report Char type
      7 Scene1 flash_scene_1 Control and report Char type
      8 Scene2 flash_scene_2 Control and report Char type
      9 Scene3 flash_scene_3 Control and report Char type
      10 Scene4 flash_scene_4 Control and report Char type

      Contact Sensor~

      DP ID Function points Identifier Data type Function type Properties
      1 Door Sensor doorcontact_state Only report Boolean
      2 Battery Level battery_percentage Only report Integer Values range:0-100, Pitch1, Scale0, Unit:%
      3 Battery Level battery_state Only report Enum Enumerated values:low, middle, high
      4 Anti-remove Alarm temper_alarm Only report Boolean

      Air purifier~

      DP ID Function points Identifier Data type Function type Properties
      1 Switch Switch Issue and report Boolean
      2 PM2.5 PM25 Only report Integer Values range:0-999, Pitch1, Scale0, Unit:
      3 Work mode Mode Issue and report Enum Enumerated values:Manual, Auto, Sleep
      4 Wind speed Speed Issue and report Enum Enumerated values:speed1, speed2, speed3, speed4, speed5
      5 Filter usage Filter Only report Integer Values range:0-100, Pitch1, Scale0, Unit:%
      6 Fresh Anion Issue and report Boolean
      7 Child lock Lock Issue and report Boolean
      9 UV light UV Issue and report Boolean
      11 Filter reset FilterReset Issue and report Boolean
      12 indoor temp Temp Only report Integer Values range:-20-50, Pitch1, Scale0, Unit:℃
      13 Indoor humidity Humidity Only report Integer Values range:0-100, Pitch1, Scale0, Unit:%

      Heater~

      DP ID Function points Identifier Data type Function type Properties
      1 Switch Power Issue and report Boolean
      2 Target temperature TempSet Issue and report Integer Values range:0-37, Pitch1, Scale0, Unit:℃
      3 Current Temperature TempCurrent Only report Integer Values range:-9-99, Pitch1, Scale0, Unit:℃
      4 Mode Mode Issue and report Enum Enumerated values:m, p
      5 Fault alarm Fault Only report Fault Barrier values:1, 2, 3
      6 Gear position gear Issue and report Enum Enumerated values:low, mid, high, off
      7 Conservation eco_mode Issue and report Boolean

      Smart fan~

      DP ID Function points Identifier Data type Function type Properties
      1 Switch switch Issue and report Boolean
      2 Wind Speed Level fan_speed Issue and report Enum Enumerated values:1, 2, 3, 4
      3 Left-and-Right Swing fan_horizontal Issue and report Enum Enumerated values:on, off
      4 Up-and-Down Swing fan_vertical Issue and report Enum Enumerated values:on, off
      5 Fault Alarm fault Only report Fault Barrier values:1, 2
      6 Anion anion Issue and report Boolean
      7 Humidify humidifier Issue and report Boolean
      8 Oxygen oxygan Issue and report Boolean
      9 Child Lock lock Issue and report Boolean
      10 Cool fan_cool Issue and report Boolean
      11 Set Temperate temp Issue and report Integer Values range:0-50, Pitch1, Scale0, Unit:℃
      12 Current Temperature temp_current Only report Integer Values range:0-50, Pitch1, Scale0, Unit:℃

      Kettle~

      DP ID Function points Identifier Data type Function type Properties
      1 Working switch start Issue and report Boolean
      2 Heat to target temperature shortcut (°C) temp_setting_quick_c Issue and report Enum Enumerated values:50, 65, 85, 90, 100
      3 Heat to target temperature shortcut (°F) temp_setting_quick_f Issue and report Enum Enumerated values:122, 149, 185, 194, 212
      4 Cool to the target temperature shortcut after boiling (°C) temp_boiling_quick_c Issue and report Enum Enumerated values:50, 65, 85, 90, 100
      5 Cool to the target temperature shortcut after boiling (°F) temp_boiling_quick_f Issue and report Enum Enumerated values:122, 149, 185, 194, 212
      6 Temperature scale switching temp_unit_convert Issue and report Enum Enumerated values:c, f
      7 Insulation switch switch_keep_warm Issue and report Boolean
      8 Holding time setting keep_warm_setting Issue and report Integer Values range:0-360, Pitch1, Scale0, Unit:min
      9 Mode work_type Issue and report Enum Enumerated values: setting_quick, boiling_quick, temp_setting, temp_

      BecaThermostat~

      Work in progress

      DP ID Function points Identifier Data type Function type Properties
      1 Switch Power Issue and report Boolean
      2 Target temperature TempSet Issue and report Integer Values range:0-37, Pitch1, Scale0, Unit:℃
      3 Current Temperature TempCurrent Only report Integer Values range:-9-99, Pitch1, Scale0, Unit:℃
      4 Mode Mode Issue and report Enum Enumerated values:m, p (wip)
      102 Floor Temperature FloorCurrent Issue and report Integer Values range:0-37, Pitch1, Scale0, Unit:℃

      Inkbird ITC-308-Wifi~

      Temperature controller with individual plug in sockets for heating/cooling

      DP ID Function points Identifier Data type Function type Properties
      101 Temperature unit Cf Issue and report Integer 0=C, 1=F
      102 Calibration Ca Issue and report Integer Unit is 0.1C
      104 Temperature sensor Issue and report Integer Unit is 0.1C
      106 Temperature set point Ts Issue and report Integer Unit is 0.1C
      108 Compressor delay time Pt Issue and report Integer Unit is minutes
      109 Alarm high temperature Ah Issue and report Integer Unit is 0.1C
      110 Alarm low temperature Al Issue and report Integer Unit is 0.1C, For negative values use -(0xFFFFFFFF - value)
      115 Relay status Only report Integer 01=cool, 02=off, 03=heating
      116 Temperature sensor Issue and report Integer Unit is 0.1F
      117 Heating differential value Hd Issue and report Integer Unit is 0.1C
      118 Cooling differential value Cd Issue and report Integer Unit is 0.1C

      The unit will constantly be sending the temperature sensor value in celsius and fahrenheit: 104 and 116. To trigger the unit to send all settings, send any value to a non-used register, e.g. TuyaSend1 2,1

      Example:

      Tasmota command Result
      TuyaSend1 2,1 Trigger the unit to reveal all settings
      TuyaSend2 106,250 Change set-point to 25.0C
      TuyaSend2 101,1 Change units to Fahrenheit

      Inkbird IHC-200-Wifi~

      Humidity controller with two relay sockets very similar to the ITC-308-WIFI This unit ships with a RTL based WR3 module which cannot be flashed with Tasmota, however the WR3 module is pin compatible with an ESP12-F module and is on a daughter board similar to the one in the ITC-308-WIFI

      DP ID Function points Identifier Data type Function type Properties
      104 Humidity sensor Only report Integer Unit is 0.1C
      106 Humidity set point HS Issue and report Integer Unit is 0.1C

      Dpid 102,108,109,110,106,117,118 return data and are not yet reverse engineered but are likely similar to ITC-308 but related to humidity.

      Further Reading~

      \ No newline at end of file diff --git a/UFS/index.html b/UFS/index.html new file mode 100644 index 0000000000..9aaa95bd95 --- /dev/null +++ b/UFS/index.html @@ -0,0 +1,15 @@ + Universal File System - Tasmota
      Skip to content

      Universal File System

      This feature is included in every precompiled binary with flash size >1M

      For ESP8266 modules that have more than 1MB of flash memory (NodeMCU, Wemos-D1) you can build a variant with a universal file system or UFS and store your files there (data, images, commands, etc). There are some special files that you can upload and use to execute actions.

      Warning

      The file partition DOES NOT get erased by reset commands. Only a complete flash erase will remove it.

      UFS in the Web GUI~

      After compiling and flashing you will find a new entry in Tasmota webUI: Consoles - Manage File system

      Manage File System button

      The "Manage File System" page provides:

      • On top, the total size of the file system and the free size
      • A button to upload a file from the host (1st select the file with the Choose File button, then Start Upload)
      • A list of available files with timestamp of upload and size in bytes
      • The 'fire' icon allows to delete the file without any confirmation
      • The 'memo' icon allows to edit the file and the "Create and edit new file" button launches the editor with a new file.

      Manage File System page

      The file editor allows to edit the content of a text file online, save it back to the UFS. By changing the name at the top, it will be saved-as the new name (original file remains unchanged). Changes can be discarded by clicking on the button "Manage File System" to returns to the manager.

      File editor

      Commands~

      Complete list of UFS commands

      Also look into Berry scripting language for ESP32 which works with UFS.

      Special files~

      autoexec.bat~

      Stores commands that will be executed at every boot, similar to the backlog commands in rules trigger at System#Boot.

      Almost any command can be used in the file. However, avoid commands that will make the device reboot, such as: changing Wifi setting, MQTT settings, Templates & GPIO, etc. Commands that triggers reboot will create a boot loop which will force Tasmota to automatically disable autoexec.bat and other settings. See SetOption36 for more details.

      Commands must be kept one command per line and they will be executed sequentially. Lines starting with semicolon are ignored as comments.

      display.bat~

      Stores data that will be displayed at every boot, similar to the DisplayText commands in rules trigger at System#Init (as long as you have a display driver initializated).

      Example

      ; clr screen
      +[z]
      +; draw full screen picture - corona.rgb file must exist in UFS storage
      +[x0y0P/corona.rgb]
      +; define index color
      +[dc19:31000]
      +; draw transparent text with new index color
      +[x60y30f2Ci19D2]Tasmota
      +

      autoexec.be~

      For ESP32 with Berry scripting language, autoexec.be file will be automatically loaded and executed at boot.

      Compiling for Universal File System~

      Copy platformio_override_sample.ini as platformio_override.ini

      For ESP8266 boards, activate by removing the ; in front of one of the below lines:

      • board = esp8266_4M3M for 3Mb universal file system
      • board = esp8266_4M2M for 2Mb universal file system
      • board = esp8266_2M1M for 1Mb universal file system

      About ESP32

      ESP32 boards with default 4MB flash only support a file system limited to 320KB. You need a board with more than 4MB to enable a larger file system.

      Extending file system size on ESP32 is performed through the board_build.partitions setting. There are preconfigured settings for 4M, 8M and 16MB ESP32 devices. The are enabled in platformio_override.ini like for the ESP8266. Comment the standard setting and uncomment the variant you want.

      ; Build variant ESP32 4M Flash, Tasmota 1856k Code/OTA, 320k LITTLEFS (UFS) (default)
      +board                   = esp32_4M
      +; Build variant ESP32 8M Flash, Tasmota 2944k Code/OTA, 2112k LITTLEFS (UFS)
      +;board                   = esp32_8M
      +; Build variant ESP32 16M Flash, Tasmota 2944k Code/OTA, 10M LITTLEFS (UFS)
      +;board                   = esp32_16M
      +
      \ No newline at end of file diff --git a/ULP/index.html b/ULP/index.html new file mode 100644 index 0000000000..33450f5d6c --- /dev/null +++ b/ULP/index.html @@ -0,0 +1,99 @@ + ULP for ESP32 - Tasmota
      Skip to content

      ULP for ESP32 ~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #define USE_BERRY_ULP      // (ESP32, ESP32S2 and ESP32S3 only) Add support for the ULP via Berry (+5k flash)
      +
      or add as a build flag to any build environment, i.e. in platformio_tasmota_cenv.ini:
      build_flags             = ${env:tasmota32_base.build_flags}
      +                        -DUSE_BERRY_ULP
      +

      Ultra Low Power coprocessor~

      The purpose of this document is not to repeat every information of these documents:
      https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ulp.html
      https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ulp_instruction_set.html

      It will also not make it easy to write assembler code for the ULP and embed it in Berry projects. But it shall guide you through the process of adapting one of many open source examples, do some little changes and setting up a toolchain for personal use cases.

      Tip

      It can even make it easier and substantially faster to rapidly develop assembler projects, because there is no flashing involved in the code deployment, which happens in Berry at runtime.

      FSM and RISCV ... what?~

      Currently there are 3 SOC's of the ESP32-family with an included ULP, which are the ESP32, the ESP32S2 and the ESP32S3.
      The oldest one - the ESP32 - features the simplest type, which Espressif names Finite State Machine or in short FSM. This ULP can only be programmed in assembly.
      The newer ESP32S2/S3 allow to run the ULP in FSM mode too with some minor additions in the instruction set. They are able ( = should be able in most cases) to use the same assembly source code, but the resulting binaries are not compatible.
      But additionally both of the new models (ESPS2/S3) are able to run the ULP in RISCV mode, which has a different toolchain and allows to use C as programming language. This makes it a lot easier to work with it. Only one ULP mode possible at a time, so you can run only in FSM or RISCV mode.
      For Tasmota it was decided to keep things as simple and modern as possible, thus for ESP32S2 and ESP32S3 the old FSM mode is not supported and we enjoy the simplicity of the high level language C.

      Bottom line for Tasmota:
      ESP32 - uses ULP in FSM mode
      ESP32S2 and ESP32S3 - use ULP in RISCV mode

      Tip

      Although the main core of the ESP32C3 uses the RISCV architecure, there is no built in ULP at all. For the upcoming ESP32C6 an integrated ULP was anounced, but no further info is available at the moment.

      Limits of the ULP~

      To simplify some things:
      Everything in the ULP is limited. On the old FSM type ULP there are only 4 registers, very few operations and limited memory access. For some operations it is not possible to use mutable values, but the code must be fixed (for pin/register access) at compile time. That's why you will see lots of defines and constants in basically every example project.
      This was the reason, why for projects like Tasmota it never made sense to include ULP code.

      Advantages of the ULP~

      Besides the possibility to run code in deep sleep and wake up the system, it can also make sense to run the ULP in parallel to the main system.
      To simplify again:
      Everything that is critical to precise timing and is somehow portable to ULP, should run better than on the main cores! This includes the internal temperature sensor and the hall sensor. Additionally it can free the main cores of some tasks.

      Data exchange between main system and ULP~

      There is a memory region which is located at fixed address 0x5000000, which is called RTC_SLOW_MEM. This is the only region that is accessible from main cores and the ULP. It is the coders job to find a way to control the data flow, by reading and writing from and to certain addresses. The toolchains down below will print out data, that will show, where accessible variable can be found to access from Berry with ULP.get_mem() and ULP.set_mem().

      General program flow~

      A typical ULP program is started from the main core at the position of the so called global entry point. Then it executes its chain of commands and ends with a halt command. It is technically possible to create a run loop inside the code and to not end with halt. But typically such a loop is realized with a wakeup timer, that restarts the with a certain interval, which can be set with ULP.wake_period(register,time in microseconds). The register is numbered from 0 to 4 and can be changed in the assembly code with sleep register.

      Tasmota conventions~

      The assembly code can be divided in different sections of which the so called .textsections contains the program, but can hold variables or arbitrary data too. In general for the assembler it is not so important, where the functions or the global entry point is located.
      But for the FSM in Tasmota the rule is, that the global entry point or a jump to it is located at position 0 in RTC_SLOW_MEM. That way ULP.run() can always point to this address 0. On RISCV ULP's the entry point is always 0.
      It is a design decision to keep the ULP module as small as possible and the addition of more internal functions shall be avoided, i.e. for doing setup of GPIO/RTC pins. If possible, this should be done in assembly code.

      Example

      // rtc_gpio_isolate(12) translates to:
      +WRITE_RTC_REG(RTC_IO_TOUCH_PAD5_REG, 27, 1, 0) //disable pullup
      +WRITE_RTC_REG(RTC_IO_TOUCH_PAD5_REG, 28, 1, 0) //disable pulldown
      +WRITE_RTC_REG(RTC_IO_TOUCH_PAD5_REG, 13, 1, 0) //disable input
      +WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TS_REG, 29, 1, 0) //disable output
      +WRITE_RTC_REG(RTC_IO_TOUCH_PAD5_REG, 31, 1, 1) //hold
      +

      Using external toolchains for this driver~

      There are 2 ways to assemble code for later use in Tasmota. In theory every external ULP project, which fits in the reserved memory space that is defined in the framework package used to compile the Tasmota firmware, should be convertible. This limit is subject to change.

      Micropython and micropython-esp32-ulp (FSM only)~

      Only available for the ESP32 using the FSM type ULP.

      A great project to run ULP code in Micropython on the ESP32 can be used to assemble and export the same projects to Tasmota.
      There are ports of Micropython for Linux, Windows and Mac, which must be installed to the system of your choice. Run it and in the Micropython console install like that:

      # MacOS
      +import upip
      +# Linux
      +import mip
      +upip.install('micropython-esp32-ulp')
      +

      After that your are ready to assemble.
      The ULP code is embedded as a multiline string in Micropython scripts. For use in Tasmota it makes sense to make some changes, that are described in an ulp_template.py and to use this template by replacing the source code string with the new code.

      Tip

      The Micropython module can not really include external headers, but it offers a very convenient database function as described here: link:preprocess
      Otherwise the missing defines must be added annually.

      After you created or did download your ulp_app.py you can export the data with 'micropython ulp_app.py' to the console, from where it can be copy pasted to the Berry console or to your Berry project.

      Tip

      It is recommended to embed the setup steps for GPIO pins or ADC to the bottom part of this ulp_app.py by printing Berry commands for easier testing in the Berry console.

      Export from ESP-IDF project~

      This is the only way, that works for every ULP version in Tasmota.

      Many projects are using the ESP-IDF with CMAKE and will be compiled with idf.py build. We can extract the ULP code without flashing this project, with two simple methods:

      Python script 'binS2Berry.py'~

      Start a helper Python binS2Berry.py script in the root level of the project, which prints the same information to console as the Micropython way.

      Web-App:~

      Use the embedded JS application right here.

      ESP-IDF build folder:

      (You can drag and drop the folder on the button too.)

      # Generate ULP code in your browser !! Parsing completely in JS, no file upload to a server.
      +

      Thus the ULP projects that may fail to assemble in Micropython can be used too. Additionally it can be helpful, to test the ULP code in a minimal working example outside of Tasmota.

      Examples~

      This is all about porting and adapting existing code. Thank you to everyone who is sharing their ULP code!!

      Let's take a look at https://github.com/micropython/micropython-esp32-ulp/blob/master/examples/blink.py.

      1. Use a copy of ulp_template.py and name it to your liking.
      2. Replace the source string of the template with the version of the example. The .textsection starts with:
        .text
        +magic: .long 0
        +state: .long 0
        +
        this must become:
        .text
        +jump entry
        +magic: .long 0
        +state: .long 0
        +
      3. This is already enough to assemble. For convenience it is recommended to add a line to the last section (with multiple "prints") with content: print("ULP.wake_period(0, 500000)").

      Done!

      Now let's modify the code slightly for different intervals for "on" and "off".

      1. Add a second wake period with print("ULP.wake_period(1, 200000)").
      2. Add sleep commands to the source code like so:
        on:
        +# turn on led (set GPIO)
        +WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TS_REG, RTC_GPIO_ENABLE_W1TS_S + gpio, 1, 1)
        +sleep 0
        +jump exit
        +
        +off:
        +# turn off led (clear GPIO)
        +WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TC_REG, RTC_GPIO_ENABLE_W1TC_S + gpio, 1, 1)
        +sleep 1
        +jump exit
        +

      The console output should look something like that:

          #You can paste the following snippet into Tasmotas Berry console:
      +    import ULP
      +    ULP.wake_period(0,500000) # on time
      +    ULP.wake_period(1,200000) # off time 
      +    var c = bytes().fromb64("dWxwAAwAaAAAAAAADAAAgAAAAAAAAAAAEACAcgEAANDlryxyMABAgCcFzBkABWgdEACAcuGvjHIBAABoIQCAcgQAANASAIByCAAgcAQAAGgBAAWCWAAAgAQFaB0AAACSZAAAgAUFaB0BAACSZAAAgAAAALA=")
      +    ULP.load(c)
      +    ULP.run()
      +

      After executing it the built in LED should blink (if wired to the usual GPIO 2). You can change the wake intervals on-the-fly with i.e. ULP.wake_period(1, 800000) in the Berry console.

      Now on to something more complex with wake from deep sleep.

      Hall sensor - FSM~

      We have a working example here: https://github.com/duff2013/ulptool/blob/master/src/ulp_examples/ulp_hall_sensor/hall_sensor.s

      Converting is possible in the same manner as before. Start with ulp_template.py, replace the string with the content of the .s file and make sure you have the include database properly populated or you add the missing defines from the header files manually.
      Additionally we need to setup the ADC pins with the help of ULP.adc_config(). In this particular example the resulting code is (in the form of print outputs placed in the .py file):

      print("ULP.adc_config(0,2,3)") # adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_6);
      +print("ULP.adc_config(3,2,3)") # adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_6); + adc1_config_width(ADC_WIDTH_BIT_12);
      +

      The entry point is already at address zero, so there are no changes needed to assemble, load and start he ULP program in Tasmota. In the console output we can find the positions of the variables which hold the voltage measurements and can read out it values:

      0000 entry
      +0051 jmp_threshold
      +0052 exit
      +0053 wake_up
      +0059 Sens_Vp0
      +0060 Sens_Vn0
      +0061 Sens_Vp1
      +0062 Sens_Vn1
      +0063 Sens_Diff_p1
      +0064 Sens_Diff_n1
      +

      In order to use this whole construction to wake the ESP32 with the help of a magnet, we now have to do some measurements to find feasible threshold values. This can be done by calculating the difference between Sens_Vpx and Sens_Vnx in Berry. Then place the magnet of your choice near the ESP32 and note how these values change. If the magnet is strong enough, chances are great, that you find a stable threshold.
      Now let's add some assembly code!

      We can add some constants in the header part of the code (that worked with a tested weak magnet):

          .set threshold_pos   , 7
      +    .set threshold_neg   , 2
      +

      Now append some variables to the end of the .bss section:

        .global Sens_Diff_p1
      +Sens_Diff_p1:
      +  .long 0
      +
      +  .global Sens_Diff_n1
      +Sens_Diff_n1:
      +  .long 0
      +

      The we need some code, which replaces line 135 and 136 of the original example:

      /* calculate differences */
      +    move r3, Sens_Vn1
      +    ld r3, r3, 0
      +    move r2, Sens_Vn0
      +    ld r2, r2, 0
      +    sub r3, r2, r3         # eventually change to sub r3, r3, r2 for your setup
      +    move r2, Sens_Diff_n1
      +    st r3,r2,0
      +    move r3, Sens_Vp1
      +    ld r3, r3, 0
      +    move r2, Sens_Vp0
      +    ld r2, r2, 0
      +    sub r3, r3, r2          # eventually change to sub r3, r2, r3 for your setup
      +    move r2, Sens_Diff_p1
      +    st r3,r2,0
      +
      +/* wake up */
      +    ld r0,r2,0 # Sens_Diff_p1
      +    JUMPR wake_up, threshold_pos, GE
      +

      After loading and starting you can send the ESP to deep sleep. For testing it is recommended to add the optional wake timer as a fallback:
      ULP.sleep(30)

      Try to wake up the system with the magnet.

      But is there a way to circumvent the limitation of this example, that forces us to set the threshold value as a constant? Well ... yes, we can do some hacky stuff.

      We must dig a little deeper, to understand how the 32-bit instructions are constructed. Let's look at the jumpr command, which is defined in ulp.h like that:

          struct {
      +        uint32_t imm : 16;          /*!< Immediate value to compare against */
      +        uint32_t cmp : 1;           /*!< Comparison to perform: B_CMP_L or B_CMP_GE */
      +        uint32_t offset : 7;        /*!< Absolute value of target PC offset w.r.t. current PC, expressed in words */
      +        uint32_t sign : 1;          /*!< Sign of target PC offset: 0: positive, 1: negative */
      +        uint32_t sub_opcode : 3;    /*!< Sub opcode (SUB_OPCODE_B) */
      +        uint32_t opcode : 4;        /*!< Opcode (OPCODE_BRANCH) */
      +    } b;                            /*!< Format of BRANCH instruction (relative address, conditional on R0) */
      +

      The constant (= immediate) value is stored in the upper 16 bits and we can access in the byte buffer. To find the address of the command we can simply add a label in the code:

        .global jmp_threshold
      +jmp_threshold:
      +    JUMPR wake_up, threshold_pos, GE
      +

      This will get printed to the console while assembling. Then in Berry we can do a:

          var c = bytes("...")
      +    # jmp_threshold is the 32-bit-address in RTC_SLOW_MEM
      +    var jmp_threshold = 51
      +    var pos = (3+jmp_threshold)*4
      +    var cmd = c[pos..pos+4]       # we do not have uint32 in Berry
      +    cmd.set(0,threshold,2)        # upper 16 bit
      +    ULP.set_mem(51,cmd.get(0, 4)) # modify running ULP program
      +

      Now we can change these constant values on the fly.

      Example driver in Berry allowing deep sleep with wake up via magnet:~

      Add commands:
      hall_thres x - shows current threshold for the p difference value or sets it to x.
      usleep x - start deep sleep for x seconds or infinitely

      ulp_hall.be

      I2C access - FSM~

      Although there are special assembler commands to access I2C devices the most common method in the examples on GitHub is bit banging. This is reported to be more reliable and circumvents some limitations (only 2 pin combinations and bytewise access with special I2C commands).
      Nearly every example is based on some very clever macros and control flow tricks, that replicate a simple stack and subroutines (similar to a library), which is a good example for the "Art of coding".
      To make it assemble in Micropython we need some functions in the Micropython-script, that can expand the macros. These functions are in a very early stage of development and might eventually later find their way into the micropython-esp32-ulp project after more refinement.

      Tip

      If your examples do not assemble in Micropython, please try out the ESP-IDF variant.

      An example for the BH-1750 light sensor can be found here: https://github.com/duff2013/ulptool/tree/master/src/ulp_examples/ulp_i2c_bitbang

      With our techniques from above the concatenation of the .s files results in: BH-1750

      Example Berry driver:~

      ulp_bh1750.be

      \ No newline at end of file diff --git a/Upgrade/index.html b/Upgrade/index.html new file mode 100644 index 0000000000..6494c19858 --- /dev/null +++ b/Upgrade/index.html @@ -0,0 +1,11 @@ + + + + + + + + +Redirecting... + + diff --git a/Upgrading/index.html b/Upgrading/index.html new file mode 100644 index 0000000000..685d906c8f --- /dev/null +++ b/Upgrading/index.html @@ -0,0 +1,22 @@ + Upgrading - Tasmota
      Skip to content

      Upgrading

      Easily upgrade Tasmota to a newer version or different build while keeping all your settings

      The first rule of upgrading: If it ain't broke, don't fix it!

      In other words, ensure that there is a good reason to mess with a working installation (e.g., a need to use a new feature or address a found problem fixed in the current version).

      Backup before upgrading

      Any time you upgrade it is highly recommended to back up your device settings. That is easily done from the webUI using Configuration - Backup Config.

      If you wish to switch to a different build or use development branch you need to download a binary file (gzipped or regular) or change the OTA Url link.

      Download binaries from:

      .gz binaries~

      Gzipped binaries can be used only once you've upgraded to atleast Tasmota 8.2

      Trying to upgrade with a gzipped binary using versions older than 8.2 will fail.

      Tasmota 8.2 introduced upgrading using gzipped binaries which are smaller in size and will likely skip the intermediary minimal build installation. This makes the upgrade process faster and straightforward.

      To use simply add .gz to the existing OTA Url or download the .bin.gz binary from the official OTA Server and the next upgrade will use the compressed file.

      Upgrade Flow~

      v1.0.11  🔀  v3.9.22  🔀  v4.2.0  🔀  v5.14.0  🔀  v6.7.1  🔀  v7.2.0  🔀  v8.5.1  🔀  v9.1  🔀  Current release

      Follow the path strictly to ensure success. Do not install only tasmota-minimal.bin but upgrade to full, working firmware. Linked -lite.bin binaries will do the job. If you want to download binaries from the upgrade flow links, you may need to do a right-click and save the file, depending on your browser security rules.

      Tasmota v9.1 introduced a major change in GPIO mapping

      Downgrading is not recommended and will probably not work correctly.

      Tasmota v8.1 introduced a major change in parameter storage.

      Downgrading is not recommended and upgrading to 8.1 has to follow the recommended path and can still fail in some cases. Don't forget to backup!

      Upgrade using webUI~

      Upgrading the device firmware over-the-air, aka OTA, is the most convenient way to upgrade.

      To start the upgrade, open a web browser to your device's web UI and select Firmware Upgrade.

      Upgrading_1

      You are presented with two choices:

      • Upgrade by webserver - use an OTA server
      • Upgrade by file upload - uploading a downloaded or self-compiled binary file from your computer

      Upgrading_2

      Upgrade by web server~

      If you want to upgrade to the latest release version click the first Start Upgrade button. This screen should appear

      Upgrading_3

      During this process Tasmota will download the new firmware from the url and install it. If you're not using a gzipped binary it might need to download tasmota-minimal.bin first, but all that happens automatically. All you have to do is wait 2 to 5 minutes.

      After the upgrade is completed you can reconnect back to the web UI and check the firmware version on the bottom of the page or in the Information tab of the webUI.

      Upgrade by file upload~

      Go to Firmware Upgrade. This time browse to the binary you want to upgrade to with Choose File and click Start upgrade.
      In our example it's tasmota-sensors.bin.

      image

      You will see an Upload starting... and then Upload successful message. Wait until the device restarts, reconnect back to the web UI and check the firmware version on the bottom of the page or in the Information tab of the webUI.

      Minimal build upgrade step

      If the binary you're upgrading with is larger than 500kb you also need to download the minimal build (tasmota-minimal.bin(.gz)) since the upload process needs the space in flash memory to upload the new binary.

      When you try to immediately upgrade with the new binary without using smaller minimal firmware you will be greeted with this error.

      Upgrading_4

      Upload Buffer Miscompare Error~

      This step is necessary only if you get the "Upload buffer miscompare" error

      This means your flash doesn't have enough free space to install the binary you're trying to upload. Use a .gz binary instead of the .bin one. If you still receive the same error download tasmota-minimal.bin(.gz) and follow instructions:

      Browse to the minimal binary with Choose File. The chosen filename should be visible.
      In our example it's tasmota-minimal.bin.

      minimal upgrade

      Wait until the device restarts. In the Main Menu web UI will display this warning message on top.

      minimal message

      Proceed to upgrade

      Using Commands~

      Your device can be upgraded using commands via MQTT, web requests or Console in the web UI.

      OtaUrl is used to set your OTA address.

      OtaUrl http://ota.tasmota.com/tasmota/tasmota-sensors.bin
      +
      In this example we chose a development branch version with additional sensors support

      Initiate upgrade from OTA server

      Upgrade 1
      +
      Wait for the upgrade process to complete and check the Tasmota version. In console you can use Status 2.

      Serial Upgrade~

      Upgrade over the serial connection using serial-to-USB adapter.

      Upload the new version over serial using the same process as in Flashing but DO NOT erase flash. The new binary will overwrite the old one and keep your settings.

      External Programs~

      Tasmota Device Manager or TDM is a multiplatform GUI application written in Python for discovery and management of Tasmota devices. You can set up OTA url and initiate OTA upgrade from TDM using GUI.

      openHAB - implement an automation rule to upgrade devices from openHAB

      Node-RED OTA server and firmware manager - Node-RED flow for managing OTA updates

      OTA over SCP - setup and configure "OTA over SCP" upload for PlatformIO

      Deploy via HTTP - deploy .bin and .bin.gz files to your own web server via PlatformIO upload

      Private OTA Server~

      It is possible to create your own simple http OTA server (https is not supported) using Python and perform upgrades from there. Install Python3 and from the folder where the binary resides (make sure tasmota-minimal.bin is located there too) run:

      python -m http.server 8000
      +
      (If the response is "No module named http" then try again with python3 instead of python.)

      Change your OtaUrl to http://ipoftheserver:8000/yourbinary.bin(.gz) and start the upgrade process. Note: do not use /, -, or . characters in the name of yourbinary.

      If your binary build (yourbinary.bin) is larger than the available free flash program space, Tasmota will need to first install the minimal version of Tasmota to make more space. To have this work via the web server OTA process, you have to copy the file tasmota-minimal.bin in the same folder where OTAURL for yourbinary.bin is placed, and rename tasmota-minimal.bin to yourbinary-minimal.bin.

      Migration Path~

      Until now several versions of Tasmota have been released starting with the C version Sonoff-MQTT-OTA followed by Sonoff-MQTT-OTA-Arduino, Sonoff-Tasmota and ultimately Tasmota.

      Intermediate upgrade steps are needed to migrate from older firmware to the current version. No migration is possible from original Sonoff-MQTT-OTA to Sonoff-MQTT-OTA-Arduino v1.0.11.

      DO NOT ATTEMPT TO UPGRADE VERSIONS PRIOR TO v7.x STRAIGHT TO THE CURRENT VERSION

      Remember that you must take each individual step between the device firmware version and the latest available.You can find all the required binaries in Tasmota Releases listed by version number.

      Tip

      As a safeguard perform "Backup Configuration" before upgrading. If settings are lost "Restore Configuration" should bring them back.

      Upgrading from one minor version to the next is mostly painless as the settings are saved in the same location in flash and newer settings are appended.

      As said, mostly painless! There are some deviations to this rule as the flash settings changed.

      Notice for versions between 6.6.0.7 and 6.6.0.11

      If you've used development versions between 6.6.0.7 and 6.6.0.11 back up your device settings. Convert the backup to human readable form as you MUST restore these settings manually.
      Perform a Reset 6 before upgrading the firmware and, for safe measure, after the upgrade completes.

      Downgrading~

      While fallback or downgrading is common practice it was never supported due to Settings additions or changes in newer releases.

      Starting with release v8.1.0 Doris settings in flash are re-allocated in such a way that fallback is only allowed and possible to v7.2.0. Once at v7.2.0 you're on your own when downgrading even further.

      Backing Up Settings~

      Tasmota uses flash memory to store options and settings. New versions add (or remove) features that use various regions of that memory. If you did not erase flash when you flashed your device, an updated version of Tasmota may be accessing areas with values left over from the old Tasmota or even the original factory firmware. This might cause unexpected and unwanted behavior or even major problems (constant reboots or reconnects).

      To avoid this use our decode-config tool to easily create and restore backups in Tasmota:

      decode-config tool~

      • decode-config - OS independent Python program to backup and restore Tasmota configuration data, also available as precompiled executables for Windows, MacOS and Linux. See Using instructions for details.
        If using one of the precompiled binary for your OS replace decode-config.py with decode-config_win32.exe, decode-config_win64.exe, decode-config_mac or decode-config_linux from the instruction examples.

      1. make a configuration backup:~

      • Create a new backup straight from your device

        decode-config.py --source <deviceIP> --backup-file Config-@f

        @f will be replaced by decode-config to device's FriendlyName

      or

      • Create a backup from previously made .dmp file

      decode-config.py --source <dmp_filename> --backup-file Config-@f

      2. perform a device reset~

      Erase flash settings area but keep Wi-Fi and MQTT settings

      Reset 6

      3. upgrade the firmware via OTA or file upload~

      4. restore configuration~

      decode-config.py --source <deviceIP> --restore-file <backupfile>

      ...and you're done!

      If you can't restore configuration directly you can configure the device manually referring to the Commands article and the settings (e.g., SetOptions, Rules, etc.) in the JSON file you created in step #1. You can paste the JSON into a JSON parser to make it easily readable.

      Tip

      If Backup Configuration -> Restore Configuration fails, reset to firmware defaults and use decode-config tool to restore your backed up configuration.

      \ No newline at end of file diff --git a/User-created-templates/index.html b/User-created-templates/index.html new file mode 100644 index 0000000000..f0288f5416 --- /dev/null +++ b/User-created-templates/index.html @@ -0,0 +1 @@ + User created templates - Tasmota
      \ No newline at end of file diff --git a/VEML6070/index.html b/VEML6070/index.html new file mode 100644 index 0000000000..8ef3f47bb3 --- /dev/null +++ b/VEML6070/index.html @@ -0,0 +1,6 @@ + VEML6070 UV light sensor - Tasmota
      Skip to content

      VEML6070 UV light sensor~

      This feature is included only in tasmota-sensors and tasmota32 binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_VEML6070
      +#define USE_VEML6070                           // [I2cDriver12] Enable VEML6070 sensor (I2C addresses 0x38 and 0x39) (+1k5 code)
      +  #define USE_VEML6070_RSET    270000          // VEML6070, Rset in Ohm used on PCB board, default 270K = 270000ohm, range for this sensor: 220K ... 1Meg
      +  #define USE_VEML6070_SHOW_RAW                // VEML6070, shows the raw value of UV-A
      +#endif
      +

      This little sensor is a great way to add UV light sensing to any microcontroller project. The VEML6070 from Vishay has a true UV A light sensor and an I2C-controlled ADC that will take readings and integrate them for you over ~60ms to 500ms." See VEML6070 UV Sensor for more information.

      Configuration~

      Wiring~

      VEML6070 ESP
      GND GND
      VCC 3.3V
      SDA GPIOx
      SCL GPIOy

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. GPIOx to I2C SDA
      2. GPIOy to I2C SCL

      Tasmota Main~

      After a reboot the driver will detect VEML6070 automatically and display UV light intensity.

      Breakout Boards~

      \ No newline at end of file diff --git a/VEML6075/index.html b/VEML6075/index.html new file mode 100644 index 0000000000..4cc933dc3a --- /dev/null +++ b/VEML6075/index.html @@ -0,0 +1,4 @@ + VEML6075 UVA/UVB/UVINDEX Sensor - Tasmota
      Skip to content

      VEML6075 UVA/UVB/UVINDEX Sensor~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_VEML6075
      +#define USE_VEML6075            // [I2cDriver49] Enable VEML6075 UVA/UVB/UVINDEX Sensor (I2C address 0x10) (+2k1 code)
      +#endif
      +

      This little sensor is a great way to add UVA and UVB light sensing to any microcontroller project. The VEML6075 from Vishay has both true UVA and UVB band light sensors and an I2C-controlled ADC that will take readings and integrate them. The sensor also comes with calibration registers so you can easily convert the UVA/UVB readings into the UV Index.

      Configuration~

      Wiring~

      VEML6075 ESP
      GND GND
      VCC 3.3V
      SDA GPIOx
      SCL GPIOy

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. GPIOx to I2C SDA
      2. GPIOy to I2C SCL

      Commands and Use~

      Console Commands Description values
      VEML6075power Power on/off the module 0/1
      VEML6075inttime UV integration time (50ms, 100ms, 200ms, 400ms, 800ms) 0/1/2/3/4
      VEML6075dynamic dynamic setting (low, high) 0/1

      Tasmota Main~

      After a reboot the driver will detect VEML6075 automatically and display UVA/UVB/UVindex intensity.

      Breakout Boards~

      VEML6075

      \ No newline at end of file diff --git a/VEML7700/index.html b/VEML7700/index.html new file mode 100644 index 0000000000..b943273756 --- /dev/null +++ b/VEML7700/index.html @@ -0,0 +1,4 @@ + VEML7700 Ambient light sensor - Tasmota
      Skip to content

      VEML7700 Ambient light sensor~

      This feature is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_VEML7700
      +#define USE_VEML7700            // [I2cDriver50] Enable VEML7700 Ambient Light sensor (I2C addresses 0x10) (+4k5 code)
      +#endif
      +

      VEML7700 is a high accuracy ambient light digital 16-bit resolution sensor in a miniature transparent 6.8 mm x 2.35 mm x 3.0 mm package. It includes a high sensitive photo diode, a low noise amplifier, a 16-bit A/D converter and supports an easy to use I2C bus communication interface. The ambient light result is available as digital value.

      Configuration~

      Wiring~

      VEML7700 ESP
      GND GND
      VCC 3.3V
      SDA GPIOx
      SCL GPIOy

      Tasmota Settings~

      In the Configuration -> Configure Module page assign:

      1. GPIOx to I2C SDA
      2. GPIOy to I2C SCL

      Commands and Use~

      Console Commands Description values
      VEML7700power Power on/off the module 0/1
      VEML7700inttime Integration time in ms 25, 50, 100,
      200, 400, 800
      VEML7700gain Gain setting
      (x1, x2, x1/8, x1/4)
      0 = x1
      1 = x2
      2 = x1/8
      3 = x1/4
      VEML7700persist ALS persistence protect number setting 0 = 1
      1 = 2
      2 = 4
      3 = 8

      With gain 1/8 and inttime 25 the sensor goes up to 120.000 Lux if someone needs it.

      Tasmota Main~

      After a reboot the driver will detect VEML7700 automatically and display light intensity.

      Breakout Boards~

      VEML7700

      \ No newline at end of file diff --git a/VL53Lxx/index.html b/VL53Lxx/index.html new file mode 100644 index 0000000000..b067e46c0e --- /dev/null +++ b/VL53Lxx/index.html @@ -0,0 +1,21 @@ + VL53L0X and VL53L1X laser ranging modules - Tasmota
      Skip to content

      VL53L0X and VL53L1X laser ranging modules~

      Support for VL53L0X is included only in tasmota-sensors and tasmota32 binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_VL53L0X 
      +#define USE_VL53L0X                            // [I2cDriver31] Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code)
      +  #define VL53L0X_XSHUT_ADDRESS 0x78           //   VL53L0X base address when used with XSHUT control
      +#endif
      +

      Support for VL53L1X is not included in precompiled binaries

      When compiling your build add the following to user_config_override.h:

      #ifndef USE_VL53L1X 
      +#define USE_VL53L1X                            // [I2cDriver54] Enable VL53L1X time of flight sensor (I2C address 0x29) using Pololu VL53L1X library (+2k9 code)
      +  #define VL53L1X_XSHUT_ADDRESS 0x78           //   VL53L1X base address when used with XSHUT control
      +  #define VL53L1X_DISTANCE_MODE Long           //   VL53L1X distance mode : Long | Medium | Short
      +#endif
      +

      The VL53L0X and VL53L1X are Time-of-Flight (ToF) laser-ranging devices from ST MicroElectronics commonly available on small modules.

      In the documentation below, VL53LXX is used whenever the information applies to either VL53L0X or VL53L1X. The exact name is used for any information that is specific to the specific model.

      The devices~

      About how to install those sensor and how to adapt the laser operation for several use-cases, please check:

      Characteristic VL53L0X VL53L1X Unit
      Max distance 2000 4000 mm
      Field of vision 25 27 °

      For more details on the supported distance range depending on the mode, the reflectance of the object and the ambient light regarding the different modes, please refer to the datasheets.

      If you are going to use long I2C wires read this article

      Breakout Boards~

      Sample boards for VL53L0X. Similar boards exist for VL53L1X. VL53L0x VL53L0x

      Configuration for single sensor~

      Wiring for single sensor~

      VL53LXX ESP
      GND GND
      VCC 3.3V
      SDA GPIOa
      SCL GPIOb

      Tasmota Settings for single sensor~

      In order to use a VL53LXX sensor you need to: - configure ESP GPIOs for I2C bus - enable the right driver

      Step 1 - Configure I2C~

      In the Configuration -> Configure Module page assign:

      1. GPIOa to I2C SDA
      2. GPIOb to I2C SCL

      On ESP8266 almost any GPIO can be used for I2C except for GPIO15. However the standard is to use GPIO4 (D1) for I2C SCL and GPIO5 (D2) for I2C SDA.

      On ESP32 any pin can be assigned to I2C.

      Step 2 - Enable the proper driver~

      I2C devices are identified on the I2C bus by their address. Because the number of possible addresses are limited (127) a lot of devices are sharing the same address. This as means that it is not possible to use simultaneously on the same I2C bus 2 devices that are using the same address. Beside, in most cases, a driver cannot correctly identify the chip it is talking to. This is why in most of the case it is important to disable Tasmota drivers for devices that you are not using and leave enabled only driver for a device you are going to use.

      In the list of I2CDEVICES supported by Tasmota it is listed that the address 0x29 can be used by either TSL2561 (driver 16), VL53L0X (driver 31), TSL2591 (driver 40) and VL53L1X (driver VL53L1X). It means that you can't use a TSL2561 or a TSL2591 at the same time as a VL53L0X/VL53L1X. And you can't use a VL53L0X at the same time as a VL53L1X.

      If you have build a tasmota binary that include the driver for TSL2561 and/or TSL2591 you must disable those drivers. You must also disable the driver for the other VL53LXX device. As a summary, here are the command to type in the console:

      • To use a VL53L0X : backlog i2cdriver16 0;i2cdriver40 0;i2cdriver31 1;i2cdriver54 0
      • To use a VL53L1X : backlog i2cdriver16 0;i2cdriver40 0;i2cdriver31 0;i2cdriver54 1

      After a reboot Tasmota will detect the VL53LXX automatically is the proper driver has been enabled.

      image

      Sensor sends a tele/%topic%/SENSOR JSON response:

      For VL53L0X:

      {"Time":"2019-12-20T11:29:22","VL53L0X":{"Distance":263}}
      +
      Or for VL53L1X
      {"Time":"2019-12-20T11:29:22","VL53L1X":{"Distance":263}}
      +

      Configuration for Multiple VL53LXX sensors in parallel~

      Tasmota supports by default up to 8 of these sensors in parallel. Note that they all must be of the same model (either all VL53L0X or all VL53L1X). In any case, the above I2C GPIO and I2C driver configuration remain valid.

      When using multiple VL53LXX, it is required to wire the XSHUT pin of each sensors to a dedicated free GPIO and assign as VL53LXX XSHUT 1 to 8. This is to let Tasmota change by software the I2C address of those and give them an unique address for operation. The sensor don't save its address and this procedure is performed automatically at every restart. The Addresses used for this are by default 0x78 (120) to 0x7F (127). As for a single sensor, you must not use any other I2C device on those addresses. As the date of writing, there is currently no I2C device supported by Tasmota with those addresses. However such devices exists and may be supported by later versions of Tasmota. You can change the relocation address by a compile option, see the compile options section.

      Wiring for Multiple Sensors~

      VL53LXX-1 VL53LXX-2 VL53LXX-3 ... ESP
      GND GND GND GND GND
      VCC VCC VCC VCC 3.3V
      SDA SDA SDA SDA GPIOa
      SCL SCL SCL SCL GPIOb
      XSHUT - - - GPIOc
      - XSHUT - - GPIOd
      - - XSHUT - GPIOe
      - - - XSHUT GPIOz

      On ESP8266, please refer to the guide Expending Tasmota for suitable GPIOs. GPIO 1 and 3 being used by serial console are generally unadvised. GPIO15 has a pull-down on every ESP board to allow proper boot. If you want to use this GPIO for a XSHUT, you must remove the pull-up resistor that is on the VL53LXX module otherwise your ESP will not boot.

      On ESP32 any GPIO that can be an output can be used.

      Tasmota Settings for multiple sensors~

      In the Configuration -> Configure Module page assign:

      1. GPIOa to I2C SDA
      2. GPIOb to I2C SCL
      3. GPIOc to XSHUT 1
      4. GPIOd to XSHUT 2
      5. GPIOe to XSHUT 3
      6. ...

      After a reboot Tasmota will detect each VL53LXX in sequence and after auto-configuring them, it will display distance in mm.

      Example: for VL53L0X it sends tele/%topic%/SENSOR JSON such as:

      {"Time":"2019-12-20T11:29:22","VL53L0X_1":{"Distance":263},"VL53L0X_2":{"Distance":344},"VL53L0X_3":{"Distance":729}}
      +

      With VL053L1X, the name of the sensor is adapted.

      The index separator is either a - if SetOption4 is 0 or a _ if it is 1. See SetOption4.

      image

      image

      image

      Compile options~

      General~

      • MAXIMUM AMOUNT OF SENSORS: Tasmota supports by default up to 8 of these sensors in parallel. Expanding this limit is possible but backwards incompatible. The default value of VL53LXX_MAX_SENSORS is set in the file tasmota.h

      • XHUT Relocation address The addresses at which the VL53LXX are relocated when using XSHUT can be changed by a define in user_config_override.h file. Replace the 0x78 for the 1st address. You need that as many consecutives address are free for the number of devices you are using. For VL53L0X use:

        #define VL53L0X_XSHUT_ADDRESS 0x78
        +
        For VL53L1X use:
        #define VL53L1X_XSHUT_ADDRESS 0x78
        +

      VL53L0X options~

      • VL53L0X LONG RANGE: By default VL53L0X reads up to 1.2 meters. If you want to use the long range mode (up to 2.2 meters), you need to add a define in user_config_override.h file:

        #define VL53L0X_LONG_RANGE
        +
        This increases the sensitivity of the sensor and extends its potential range, but increases the likelihood of getting an inaccurate reading because of reflections from objects other than the intended target. It works best in dark conditions.

      • VL53L0X_HIGH_SPEED vs VL53L0X_HIGH_ACCURACY Either or the other can be added in user_config_override.h file to change the default compromise to either proceed at higher speed (but at the cost of accuracy) or at higher accuracy.

        #define VL53L0X_HIGH_SPEED
        +
        or
        #define VL53L0X_HIGH_ACCURACY
        +

      VL53L1X options~

      • Distance range can be changed between Long, Medium or Short by copying one (and only one) of the below lines in user_config_override.h. The default value is Long. For details on those range, please refer to the datasheet of the device.
        #define VL53L1X_DISTANCE_MODE Long
        +#define VL53L1X_DISTANCE_MODE Medium
        +#define VL53L1X_DISTANCE_MODE Short
        +
      \ No newline at end of file diff --git a/Visual-Studio-Code/index.html b/Visual-Studio-Code/index.html new file mode 100644 index 0000000000..ffc260a3e8 --- /dev/null +++ b/Visual-Studio-Code/index.html @@ -0,0 +1,22 @@ + Visual Studio Code - Tasmota
      Skip to content

      Visual Studio Code

      How to setup and configure Visual Studio Code with PlatformIO for Tasmota compilation and upload.

      Easy way (only Windows): Portable install of Visual Studio Code for Tasmota~

      Download the ready made Portable Installation of VSC/PlatformIO and extract the ZIP to a folder or a fast extern drive.
      Grab Tasmota unpack and Start VS Code.exe (in folder VSC)

      Full Install (Windows, Linux and Mac)~

      Download and Install Visual Studio Code~

      Download Visual Studio Code (VSC) from https://code.visualstudio.com/

      Install PlatformIO Extension~

      Install the PlatformIO IDE extension in VSC.

      Select View - Extensions and type PlatformIO in the search box.

      Make sure to select the official PlatformIO.org PlatformIO IDE extension and select Install. Accept to install dependencies.

      Download Tasmota~

      Download the latest Tasmota version from https://github.com/arendst/Tasmota and unzip to a known folder.

      Copy files~

      Copy all files from the Tasmota Source code into your VSC working folder.

      Compile Tasmota~

      Start VSC and select File - Open Folder... to point to the working folder.

      Note: Press Ctrl + Shift + P and type PlatformIO to see all options.

      Select the desired firmware via VSC menu.

      Easy compilation can be performed from the icons at the bottom of the VSC screen.

      Upload Tasmota~

      Enable desired options in platformio.ini for serial upload like:

      ; *** Upload Serial reset method for Wemos and NodeMCU
      +upload_port = COM5
      +;upload_speed = 512000
      +upload_speed = 115200
      +;upload_resetmethod = nodemcu
      +

      deploy via HTTP upload~

      Special options (not needed for compiling Tasmota!) are enabled in platformio_override.ini :

      ; *** Upload file to OTA server using HTTP
      +upload_port = -i domus1 -p 80 -u /api/upload-arduino.php
      +extra_scripts = ${esp_defaults.extra_scripts} pio/http-uploader.py
      +
      For ESP32 replace esp_defaults with esp32_defaults.

      Or if you wish to upload the gzip files (not for ESP32 only for ESP8266):

      upload_port = -i domus1 -p 80 -u /api/upload-arduino.php
      +extra_scripts = ${esp_defaults.extra_scripts} pio/http-gz-uploader.py
      +
      Easy compilation and upload can be performed from the icons at the bottom of the VSC screen or use Ctrl + Alt + U to upload (will build if needed).

      The upload.php file at your webserver could look like this:

      <?php
      +$image = basename($_FILES["file"]["name"]);
      +$target_file = "./".$image;
      +$hostname = $_SERVER['SERVER_NAME'];
      +
      +if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_file)) {
      +  echo "The file $image has been uploaded to OTA server $hostname. \n";
      +} else {
      +  echo "Sorry, there was an error uploading your file $image to OTA server $hostname. \n";
      +}
      +?>
      +

      Hint:~

      In case vscode shows a huge amount of errors using PlatformIO - Intellisense a possible "solution" is to change the cpp-Intelli Sense Engine type to "TAG PARSER"

      This setting can be changed in workspace settings by: Use Ctrl + Shift + P and type Preferences: Open Workspace Settings and type intelli Sense in the search box. Now change the value for Intelli Sense Engine to Tag Parser.

      \ No newline at end of file diff --git a/WS2812B-RGB-Shield/index.html b/WS2812B-RGB-Shield/index.html new file mode 100644 index 0000000000..54555c3bbd --- /dev/null +++ b/WS2812B-RGB-Shield/index.html @@ -0,0 +1 @@ + WS2812B RGB Shield - Tasmota
      Skip to content

      WS2812B RGB Shield

      WS2812B RGB Shield (single pin)~

      From the Wemos WS2812b shield specs the DATA pin is connected to D2 of the Wemos.

      Tasmota Settings~

      In Configuration -> Configure Module page assign:

      • D2 GPIO4 to WS2812

      After a reboot of the device the toggle button and light controls are displayed in the webUI.

      \ No newline at end of file diff --git a/WS2812B-and-WS2813/index.html b/WS2812B-and-WS2813/index.html new file mode 100644 index 0000000000..6501eb3c4b --- /dev/null +++ b/WS2812B-and-WS2813/index.html @@ -0,0 +1 @@ + WS2812B and WS2813 - Tasmota
      Skip to content

      WS2812B and WS2813

      WS2813B versus WS2812B~

      The newer version of ws2812b is the ws2813b, which has dual signal lines to prevent a led failure also consecutive leds fail.

      ESP8266 needs Level shifting - WS281x schematic~

      Below the schematics to connect an ESP82xx to a WS281x.

      About this circuit diagram and the oscilloscope traces~

      Channel 1 (yellow) is connected to an output of the ESP8266 Chip, and therefore shows the input voltage of the level shifting circuit.

      Channel 2 (green) is connected to the output of the level shifting circuit, and therefore shows the input voltage to the first WS281X pixel.

      How does the MOSFET circuit work and what are the challenges?

      When the GPIO is HIGH (3.3V), the voltage between Gate and Source of the FET is zero since the Gate is tied to +3.3V through R4 and the Source follows the GPIO. The FET is not conducting between its Source and Drain connections. In this state the Output is pulled to +5V by R5.

      (BTW: R4 is only there to charge/discharge the parasitic input capacitance of the FET and could be omitted entirely for an ideal FET.)

      When the GPIO goes low, it pulls the Source of the FET to GND (0V). That increases the voltage across Gate and Source of the FET (VGS) to 3.3V, causing the FET to become conductive between Source and Drain, and consequently pull the output to GND.

      The major flaw of this circuit is that its usefulness degrades towards higher frequencies (shorter pulses) and capacitive loads because the only force pulling the output to HIGH is the pull-up resistor R5. So the load capacitance gets charged through R5 only. You can mitigate the effect partially by selecting a smaller R5 but the tradeoff will be increased power consumption of the circuit.

      A WS2813 signal input imposes a capacitive load of 15 pF onto the level shifter’s output according to its datasheet. That doesn’t sound like a lot, but you can see from the plots that we have only few nanoseconds time to charge the parasitic capacitor. It also partially adds up with the reverse transfer capacitance of the BSS138 FET (Crss=6pF according to the datasheet).

      The result is clearly visible in the oscilloscope plot: the leading edges are not sharp and therefore reducing the pulse duration acquired by the pixel’s input, depending on the input voltage threshold of the pixel (0.7 x VDD = 3.5V @ 5V according to the datasheet). The effect of sampled pulse shortening gets worse with rising input HIGH threshold of the pixel.

      NB: The design frequency of this circuit would be 1 / (433 ns x 2) = 1.155 MHz

      So what would be a better solution?

      Better Circuit: TTL logic gates

      We need a circuit with well-defined and matching input voltage thresholds. TTL gates represent such circuits. For a power supply of 5V, a TTL input is guaranteed logic LOW when the input voltage is below 0.8V. That same input is guaranteed logic HIGH when the input voltage is above 2.0V.

      That perfectly matches our 3.3V GPIO output voltage!

      The output weakness of the MOSFET circuit does not apply to a TTL gate’s output since it can drive to HIGH as well as to LOW by means of a circuit known as “Totem Pole” push-pull transistor output stage. 74HCT and 74AHCT chips will drive the output to a voltage close to VCC.

      This output stage gives much sharper leading edges as can be seen on the lower right oscilloscope picture.

      So this is actually the circuit I would suggest for the application. A large variety of TTL chips can be used: 74HCTXX series, 74AHCTXX series, but not 74HC or 74AHC!

      Which logic function you choose is up to your liking and availability in your parts bin. The input characteristics are the same for all of them (with a slight deviation for those with Schmitt trigger inputs like the 74XX14).

      • 5V power supply: the power of the power supply depends on the number of leds. A pixel has 3 leds (RGB), each led drains a max of 20mA, so a pixel drains a max of 60mA (3 * 20mA). So the total current that the power supply must deliver is 60mA times the number of pixels. For example 80 pixels: 60mA * 80 = 4800mA = 4.8A. Also the ESP8266 is powered by the power supply so in this example a power supply of 6A (30W) will do.

      note: connect the ledstrip from both sides to the power supply

      Tasmota Configuration~

      In the Configuration -> Configure Module page, select the following:

      • D1 GPIO5 : WS2812

      Tasmota Main~

      \ No newline at end of file diff --git a/WebUI/index.html b/WebUI/index.html new file mode 100644 index 0000000000..3bf03579f1 --- /dev/null +++ b/WebUI/index.html @@ -0,0 +1,8 @@ + WebUI - Tasmota
      Skip to content

      WebUI

      Tasmota's web user interface is a practical way to control and manage your tasmotized device.

      Warning

      WebUI does not and can not have all the features and commands implemented. For precise and complete control use Console or MQTT commands!

      To access the WebUI use your device's IP address in your favorite web browser.

      By default, WebUI starts in unprotected admin mode which allows complete access to your device to anyone with access to that IP.

      If you want to restrict other users to only control through the WebUI, use WebServer 1.

      Command WebPassword will set up a password that needs to be entered when trying to access WebUI. This is a very thin layer of protection since everything is transmitted over unencrypted HTTP protocol.

      Configuration~

      Configuration menu allows you to configure everything from components to Wi-Fi and gives you the option to backup and restore the configuration in a secure location.

      Information~

      Displays a single page loaded with information about the device including: current Tasmota version, Wi-Fi AP data, MQTT host data, and more

      Firmware Upgrade~

      An easy to use menu to initiate a firmware upgrade from an uploaded .bin file or an OTA server.

      Console~

      Terminal access to Tasmota. Issue commands here or follow the information stream. Useful for debugging when using ˙Weblog 4`.

      Customizing~

      WebButton~

      Change names of the toggle buttons using WebButton.

      Themes~

      WebUI is themable using WebColor commands.

      To apply a theme from below copy the entire code block and send it using the console or via MQTT.

      Dark (default theme)~

      WebColor {"WebColor":["#eaeaea","#252525","#4f4f4f","#000000","#dddddd","#65c115","#1f1f1f","#ff5661","#008000","#faffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#faffff","#999999","#eaeaea"]}
      +

      Light (default until 6.7.1.)~

      WebColor {"WebColor":["#000000","#ffffff","#f2f2f2","#000000","#ffffff","#000000","#ffffff","#ff0000","#008000","#ffffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999","#000000"]}
      +

      Halloween~

      WebColor {"WebColor":["#cccccc","#2f3133","#3d3f41","#dddddd","#293134","#ffb000","#293134","#ff5661","#008000","#ffffff","#ec7600","#bf5f00","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999","#bc4d90"]}
      +

      WebColor {"WebColor":["#e0e0c0","#000033","#4f4f4f","#000000","#dddddd","#a7f432","#1e1e1e","#ff0000","#008000","#ffffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999","#eedd77"]}
      +


      Purple Rain~

      WebColor {"WebColor":["#eaeaea","#252525","#282531","#eaeaea","#282531","#d7ccff","#1d1b26","#ff5661","#008000","#faffff","#694fa8","#4d3e7f","#b73d5d","#822c43","#1f917c","#156353","#faffff","#716b7f","#eaeaea"]}
      +


      Solarized Dark~

      WebColor {"WebColor":["#839496","#002b36","#073642","#839496","#002b36","#839496","#073642","#b58900","#859900","#eee8d5","#268bd2","#185886","#dc322f","#90211f","#859900","#647300","#839496","#073642","#839496"]}
      +


      Tip

      When using an MQTT client such as mosquitto_pub, enclose the message payload in single quotes (')

      mosquitto_pub -h 192.168.1.20 -t "cmnd/myTopic/WebColor" -m '{"WebColor":["#eaeaea","#252525","#4f4f4f","#000000","#dddddd","#65c115","#1f1f1f","#ff5661","#008000","#faffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#faffff","#999999","#eaeaea"]}'
      +
      \ No newline at end of file diff --git a/White-Blend-Mode/index.html b/White-Blend-Mode/index.html new file mode 100644 index 0000000000..3423b283e8 --- /dev/null +++ b/White-Blend-Mode/index.html @@ -0,0 +1 @@ + White Blend Mode for lights - Tasmota
      Skip to content

      White Blend Mode for lights~

      White Blend Mode is used for 4 channel (RGBW) and 5 channel (RGBWC) devices. It is enabled by setting the last PWM channel to zero (e.g., RGBWWTable 255,255,255,<n>,0) to lower the white channel intensity.

      Generally, white LEDs are brighter than RGB LEDs. If you want to keep the same brightness, you need to calibrate the white level. In this mode, any white component will be removed from RGB LEDs and sent to the white LEDs. This makes whites look much better.

      Example: Color 30508000 will be converted to Color 0020503000 (0x30 is subtracted from R,G,B channels and added to the White channel)

      To calibrate:

      1. Color FFFFFF00
      2. RGBWWTable 255,255,255,255,255 - reset to RGB mode
      3. RGBWWTable 255,255,255,<n>,0 (begin the calibration process with <n> == 150)
      4. If too bright, decrease <n>. If too dim, increase <n>
      5. Go back to step 2 and iterate until satisfied with the color intensities.

      Examples: * Sonoff B1: RGBWWTable 255,255,255,35,0 * Teckin SB50: RGBWWTable 255,255,255,80,0

      \ No newline at end of file diff --git a/WifiPower/index.html b/WifiPower/index.html new file mode 100644 index 0000000000..0180e6857c --- /dev/null +++ b/WifiPower/index.html @@ -0,0 +1 @@ + WifiPower Command - Tasmota
      Skip to content

      WifiPower Command~

      WifiPower allows you to fine tune the Wi-Fi transmission power level. The default is 17dBm which should be enough power for the device to transmit to the Wi-Fi access point (AP) in a normal network environment use case. Changing this setting will impact the Wi-Fi range of the device. The general rule of thumb is for every 3dBm change up or down will double or halve the range, respectively. However, this is largely dependent on the Wi-Fi router's capabilities.

      Use the RSSI signal level reported by the router for a particular Tasmota device to adjust the power level of a device. Lower the value incrementally until you achieve a balance between connection stability and energy saving. In some cases a device may require slightly more power to maintain a stable connection to the Wi-Fi network. In this case, increment the value in 1 dBm increments until stable connectivity is observed. ⚠ Do not exceed 17dBm! ⚠ Exceeding the default 17dBm transmit power setting may cause unreliable device operation. Most devices have been designed with the 17dBm theoretical power setting and may not have the ability to dissipate the additional heat generated. Setting the transmit power too high may cause interference in the device antenna causing Wi-Fi reception problems.

      This setting will not affect the signal level received from the AP (i.e., the RSSI reading that Tasmota reports).

      Improper setting of this parameter may cause operational instability and can generate exceptions. Increasing WifiPower puts additional demand on the device electrical power supply. Exceeding the capabilities of the power supply can cause other erratic device behaviors. Of course, the opposite may be the case when reducing transmit power levels allowing a device with a borderline power regulator to operate reliably.

      You should perform substantial testing and monitoring to find the sweet spot for WifiPower.

      \ No newline at end of file diff --git a/Zigbee-CCLib-Flashing/index.html b/Zigbee-CCLib-Flashing/index.html new file mode 100644 index 0000000000..5028c574b1 --- /dev/null +++ b/Zigbee-CCLib-Flashing/index.html @@ -0,0 +1,66 @@ + Zigbee CCLib Flashing - Tasmota
      Skip to content

      Zigbee CCLib Flashing

      Flash using CClib~

      To simplify this procedure, a ready to use fork of the needed firmware files is available.

      Flash CCLib on an ESP82xx Device~

      Flashing the CC2530 normally requires a CC_DEBUGGER. Using an ESP82xx device like a Wemos D1 Mini is a lower cost alternative.

      If you are using a Wemos D1 Mini or NodeMCU, just plug the microUSB port. Vcc (3.3V), GND, Tx (GPIO1), and Rx (GPIO3) are connected via the microUSB port. Be sure that you are using a USB data cable.

      For ESP devices that do not have a microUSB connector, make the following connections:


      ESP
      Device
      Serial
      Programming
      Adapter
      Vcc Vcc
      GND GND
      GPIO0 GND
      GPIO1 Rx
      GPIO3 Tx

      Follow the usual ESP82xx flashing process - you are just using CCLib_proxy.ino.bin instead of Tasmota.

      Once the firmware upload completes, retain the serial interface connections (3.3V, GND, Tx, Rx). These will be used later for flashing the CC2530.

      C. Flash a DL-20 Zigbee module The DL-20 Zigbee module has a 5-pin 1.27mm pitch unpopulated header with 0.6mm througholes. For flashing any of the Zigbee modules, you need the following connections:

      ESP
      Pin
      D1 Mini
      NodeMCU
      CC2530
      Pin
      DL-20 J2
      Pin Location
      GPIO12 D6 CC_DD
      (A.K.A. P2_1 ('P21') or Debug Data)
      5
      GPIO4 D2 CC_DC
      (A.K.A. P2_2 ('P22') or Debug Clock)
      4
      Vcc 3.3v Vcc 3
      GPIO5 D1 CC_RST 2
      GND GND GND 1

      DL-20 Flashing Jumpers
      Insert alternating male Dupont jumpers; one jumper on one side, the next one on other side. This allows the pins to provide the friction themselves to maintain contact and remain firmly in place. You only need DD, DC, and RST (a fourth jumper is shown which is used to keep the RST jumper in place). Vcc and GND are available on the main serial interface pins.

      D. Upload the firmware to the CC2530
      The CC2530 requires Z-Stack_Home_1.2, of type Default (not Source Routing). For convenience, ready to use firmware files are provided. Select the right one for your hardware: CC2530, CC2530 + CC2591 or CC2530 + CC2592.

      These Python scripts require Python 2.7.
      1) Ensure that you have Python 2.7 installed

      2) Install pyserial 3.0.1:

      pip install pyserial==3.0.1
      +

      3) Check for connectivity before flashing:

      python Python/cc_info.py -p <serial_port>
      +

      where <serial_port> is the serial port for the ESP82xx device. e.g. /dev/cu.usbserial-xxxx or COM7

      Example of result:

      INFO: Found a CC2530 chip on /dev/cu.usbserial-xxxx
      +
      +Chip information:
      +      Chip ID : 0xa524
      +   Flash size : 16 Kb
      +    Page size : 2 Kb
      +    SRAM size : 1 Kb
      +          USB : No
      +
      +Device information:
      + IEEE Address : 000000000000
      +           PC : 0000
      +
      +Debug status:
      + [ ] CHIP_ERASE_BUSY
      + [ ] PCON_IDLE
      + [X] CPU_HALTED
      + [ ] PM_ACTIVE
      + [ ] HALT_STATUS
      + [X] DEBUG_LOCKED
      + [X] OSCILLATOR_STABLE
      + [ ] STACK_OVERFLOW
      +
      +Debug config:
      + [ ] SOFT_POWER_MODE
      + [ ] TIMERS_OFF
      + [ ] DMA_PAUSE
      + [ ] TIMER_SUSPEND
      +

      If your CC2530 is DEBUG_LOCKED, then the flash size will be incorrectly reported as 16kB. Dont worry flashing the Z-Stack firmware will work and reset the DEBUG_LOCKED bit.

      In some situation flashing fails with the error message flash have not enough space. If this happens do the following:

      python Python/cc_read_flash.py -p <serial_port> -o x.hex
      +python Python/cc_write_flash.py --erase -p <serial_port> -i x.hex
      +
      Recheck for connectivity and the correct flash size by repeating step #3.

      4) Flash the Z-Stack firmware using the following command:

      Flashing the CC2530 takes about 20 minutes

      python Python/cc_write_flash.py -e -p <serial_port> -i Bin/CC2530_DEFAULT_20190608_CC2530ZNP-Prod.hex
      +
      INFO: Found a CC2530 chip on /dev/cu.usbserial-xxxx
      +
      +Chip information:
      + Chip ID : 0xa524
      +Flash size : 256 Kb
      +Page size : 2 Kb
      +SRAM size : 8 Kb
      +     USB : No
      +Sections in Bin/CC2530_DEFAULT_20190608_CC2530ZNP-Prod.hex:
      +
      +Addr.    Size
      +-------- -------------
      +0x0000   8176 B 
      +0x1ff6   10 B 
      +0x3fff0   1 B 
      +0x2000   239616 B 
      +
      +This is going to ERASE and REPROGRAM the chip. Are you sure? <y/N>:  y
      +
      +Flashing:
      +- Chip erase...
      +- Flashing 4 memory blocks...
      +-> 0x0000 : 8176 bytes 
      +Progress 100%... OK
      +-> 0x1ff6 : 10 bytes 
      +Progress 100%... OK
      +-> 0x3fff0 : 1 bytes 
      +Progress 100%... OK
      +-> 0x2000 : 239616 bytes 
      +Progress 100%... OK
      +
      +Completed
      +

      If you don't see any on screen activity that flashing has begun (i.e., progress percentages increasing) within a couple minutes, then abort the command, cycle power on the ESP82xx, and start this step over.

      Described in greater detail in this blog post.

      \ No newline at end of file diff --git a/Zigbee-Internals/index.html b/Zigbee-Internals/index.html new file mode 100644 index 0000000000..b4c795edc5 --- /dev/null +++ b/Zigbee-Internals/index.html @@ -0,0 +1,5 @@ + Zigbee internals - Tasmota
      Skip to content

      Zigbee internals~

      Back to Zigbee

      This page is for developers who want to understand how Zigbee2Tasmota (Z2T) works and its implementation details.

      CC2530 Serial protocol~

      The CC2530 is flashed with Texas Instrument ZNP Software version 1.2. The protocol is build on a serial communication between the main cpu and the CC2530.

      Z-Stack 1.2 Monitor and Test API

      Serial communication is configured as 8N1, 115200 bauds. We suggest to use GPIO13/15 because they have hardware serial support. Please note that there is only one usable hardware serial, either on GPIO1/3 or GPIO13/15.

      To enable hardware serial on GPIO13/15 for Tasmota, set Serial 0 and restart. Otherwise Z2T will use Software serial provided by TasmotaSerial.

      Receiving 115200 bauds in software is a timing challenge. Here is the anatomy of a byte transmitted in serial mode. Each bit is 8.7µs, or ~700 CPU cycles at 80MHz (1400 cycles at 160MHz).

      It all starts with a LOW "start bit" that generates an interrupt transferred to TasmotaSerial. Then TasmotaSerial enters a tightly controlled loop to read each bit (least sifnificant first). The frame stops with a HIGH stop bit.

      Typical serial frame

      What can go wrong? Tasmota may be already handling an interrupt when the start bit arrives, potentially causing a shift by 1 bit and a wrong message.

      Here is a 0xFE byte correctly received:

      Frame 0xFE correctly read

      Same frame with a delay in the interrupt handler, and mistakenly read 0xFF:

      Frame 0xFE incorrectly read to 0xFF

      TasmotaSerial has been improved to allow receiving a train of bytes withtout any disruption.

      CC2530 generally sends all the bytes one after the other for a single ZNP message (up to 250 bytes). Instead of giving back control after the first byte, the TasmotaSerial interrupt handler continues to monitor the serial RX line for the next 4 bits and checks whether a new start bit arrived. It avoids any error after the first byte was received.

      Tasmota Serial chaining bytes without releasing interrupts

      Still the first byte in the message could have been wrong. Fortunately, the first byte sent by ZNP is always 0xFE (see below). This means that if the interrupt arrives too late, Tasmota will read 0xFF instead of 0xFE. Z2T software does automatic error correction in this case, i.e. if the first byte received is 0xFF, it is automatically assumed to be 0xFE and the rest of the message is read normally.

      With these two schemes, software serial for Zigbee proved to be extremely reliable, even at 80MHz. It is highly recommended though to run at 160MHz.

      State machine - CC2530 initialization and configuration~

      After Tasmota boots, it sends the sequence 410001 to force a CC2530 hardware reset. Z2T implements an asynchronous state machine to handle the initialization and configuration of the CC2530. The state machine sends commands and waits for responses (with time-outs) and takes different branches depending on the responses.

      Instruction set~

      The program is encoded as a list of 32 bits instructions, stored in Flash in the zb_prog array of type Zigbee_Instruction[]. There is a PC (program counter) that is increased at each tick (i.e. every 50ms).

      The state machine has very simple instructions.

      Instructions encoded with 4 bytes:

      • NOOP: no-operation, do nothing and move to the next instruction
      • LABEL(x): no-operation, and defines a Label (8 bits) that can be called by code.
      • GOTO(x): moves the PC to the instruction with LABEL(x)
      • ZI_ON_ERROR_GOTO(x): if an error occurs, move to label
      • ZI_ON_TIMEOUT_GOTO(x): if a timeout occurs, move to label
      • WAIT(y): wait for milliseconds (unsigned 16 bits). Note the granularity is 50ms and the wait is non-blocking
      • WAIT_FOREVER: pause the state machine and wait for an external goto
      • STOP: stop completely the state machine, only used after an unrecoverable error

      Instructions encoded with 8 bytes:

      • CALL(f, x): call a function, is the address of the function of type uint32_t f(uint8_t). The input parameter is . The response is according to callbacks responses, except -1 (time-out) simply continues the flow.
      • LOG(m): log the string . can be in PROGMEM. For debugging only.
      • MQTT_STATE(x, m): sends a MQTT ZbState message, with status code and message . can be in PROGMEM.
      • SEND(d): send a ZNP sequence to CC2530. is an array of , a macro computes automatically the size of the array. can be in PROGMEM.
      • WAIT_RECV(x, m): wait for a specific message to be received with a time-out of (uint16_t). Messages take into account are owly those matching the first 2 bytes. The complete message match is expected or an error is generated. If the message received is longer than , additional bytes are ignored
      • WAIT_UNTIL(x, m): similar to WAIT_RECV but message that don't match are ignored, until a matching message is received.
      • ON_RECV_UNEXPECTED(f): if we received an unexpected (or unsupported) zigbee message, call function

      Instructions encoded with 12 bytes:

      • WAIT_RECV_FUNC(x, m, f): similar to WAIT_RECV and a function is called when the message matches.

      All callbacks return int32_t with the following effect:

      • > 0: goto the corresponding label
      • 0: continue
      • -1: signal a time-out
      • < -1: trigger an error (goto on_error)

      Initialization code for the state machine~

      At Tasmota start-up, the state-machine fires. The current Z2T pseudo-code does the following:

      Init:

      • Set-up all the error handling functions
      • Wait for 10.5 seconds after boot
      • Send a RESET to CC2530
      • Wait for CC2530 boot

      Check configuration (if something is wrong, go to CONFIGURE):

      • Check if the CC2530 was previously configured. It uses the same 1-byte Non-Volatile 0xF00 address and stores 0x55.
      • Checks the Z-Stack version
      • Checks the internal configuration: PanID, Ext PanID, Channel, PFGK and PFGKEN.
      • If all good, emit an MQTT message saying Zigbee is configured
      • Goto Start

      Configure (only if needed):

      • Emit an MQTT message for reconfiguration
      • Do a factory reset of CC2530
      • Reset the device once again
      • Configure the following: PanId, Ext PanId, Channel, set type to Coordinator, PFKEY, PFKEYEN, Security Module
      • Create NF 0xF00 location and store 0x55
      • Goto Start

      Start:

      • Wait for CC2530 message saying the coordinator successfully started
      • Query DeviceInfo
      • Query Node Descriptor
      • Query Active Endpoints
      • Register 2 endpoints with profile 0x0104 (Home Automation) : 0x01 (default), 0x0B (for Xiaomi)
      • Query Active Endpoints to verify 0x01 and 0x0B are active
      • Close PermitJoin: don't accept any pairing
      • Emit an MQTT message to indicate Zigbee started
      • Mark Zigbee as initialized, accept incoming messages
      • Load device configuration from Flash
      • Query any lights declared with ZbLight to read their current states
      • Pause the state machine

      Pairing devices~

      When you open pairing with ZbPermitJoin 1 (60 seconds) or ZbPermitJoin 99 (until next reboot), you allow new devices to join the network.

      Example below is for an OSRAM Plug.

      When a new devices joins, Z2T receives a TC Device Indication: ZDO_TC_DEV_IND (45CA) message with the device short (16 bits) address and IEEEAddress (64 bits).

      16:39:26 MQT: tele/Zigbee_home/RESULT = {"ZbState":{"Status":30,"IEEEAddr":"0x7CB03EAA0A0292DD","ShortAddr":"0xF75D","PowerSource":true,"ReceiveWhenIdle":true,"Security":false}}
      +

      Z2T then queries the device for additional information, like ZbProbe would do.

      First probe for Active Endpoint ZDO_ACTIVE_EP_REQ

      16:39:26 MQT: tele/Zigbee_home/RESULT = {"ZbState":{"Status":32,"ActiveEndpoints":["0x03"]}}
      +

      Finally query for the following general attributes: Manufacturer Id and Model Id.

      16:39:26 ZIG: ZbZCLRawReceived: {"0xF75D":{"0000/0004":"OSRAM","0000/0005":"Plug 01"}}
      +16:39:26 MQT: tele/tasmota/Zigbee_home/SENSOR = {"ZbReceived":{"0xF75D":{"Manufacturer":"OSRAM","ModelId":"Plug 01","Endpoint":3,"LinkQuality":36}}}
      +

      Code flow when a message is received~

      Message Serial decoding~

      Here is a detailed view of the code flow and transformations applied when a Zigbee message is received. It's simple but has many ramifications.

      During the Tasmota event loop, Z2T first checks any incoming message by calling ZigbeeInputLoop(), and after parsing incoming messages, it sends any outgoing message by calling ZigbeeOutputLoop().

      Note: outgoing messages are not sent directly but stacked into a buffer and sent once per event tick. This avoids lost messages when sending them too fast.

      For ZNP, the serial buffer is read if there is any incoming data. The message is checked for checksum and put into a SBuffer object of maximum size of 256 bytes. If a message is ready, it calls ZigbeeProcessInput(znp_buffer)

      For EZSP, the flow is a little more complex because multiple layers of decoding are required. The first layer receives the message and handles UART-EZSP protocol messages: ignores XON/XOFF, decodes ESCAPE characters, CANCEL... It then decodes according to the pseudo-random generator, and checks the final CRC. If ok, it calls the second stage via ZigbeeProcessInputRaw(ezsp_buffer).

      Note: the green light of the ZBBridge Led_i 1 is set to blink when a message is received from EZSP (which does not mean an actual Zigbee radio message was received).

      EZSP second stage decodes the ASH protocol, including ACK/NAK of messages, RSTACK (reset confirmation) and ERROR. In case of ERROR, the EZSP stack is not able to respond anymore and requires a complete reset. In this case a log entry is produced and the entire Tasmota is automatically restarted. This stage automatically sends ACK messages to confirm reception of messages. If a DATA frame is received, it then calls the third stage via ZigbeeProcessInputEZSP(buf).

      The third stage of EZSP decoding extracts the message, logs if needed and then calls ZigbeeProcessInput(buf).

      State machine handling~

      The message is passed to the state machine that will either automatically match the message and pass to the next state, or pass it to the default handler.

      When the stack is fully initialized, zigbee.init_phase == false, the default handler is ZNP_Recv_Default() for ZNP or EZ_Recv_Default() for EZSP.

      For ZNP, ZDO messages are dispatched to the relevant handlers: ZDO_END_DEVICE_ANNCE_IND, ZDO_TC_DEV_IND, ZDO_PERMIT_JOIN_IND, ZDO_NODE_DESC_RSP, ZDO_ACTIVE_EP_RSP, ZDO_SIMPLE_DESC_RSP, ZDO_IEEE_ADDR_RSP, ZDO_BIND_RSP, ZDO_UNBIND_RSP, ZDO_MGMT_LQI_RSP, ZDO_MGMT_BIND_RSP. Note: PARENT_ANNCE is handled at ZNP level and not passed to the application.

      AF_DATA_CONFIRM emits a log message, and data messages are handled in ZNP_ReceiveAfIncomingMessage(). The ZCL frame is decoded into a ZCLFrame object and sent to Z_IncomingMessage().

      For EZSP, messages are directly dispatched for trustCenterJoinHandler, incomingRouteErrorHandler, permitJoining and messageSentHandler. All other incoming messages, including ZDO, are sent to EZ_IncomingMessage().

      EZSP: EZ_IncomingMessage() then decodes ZDO messages and dispatches them: ZDO_Device_annce, ZDO_Active_EP_rsp, ZDO_IEEE_addr_rsp, ZDO_Simple_Desc_rsp, ZDO_Bind_rsp, Z_UnbindRsp, Z_MgmtLqiRsp, Z_MgmtBindRsp, ZDO_Parent_annce, ZDO_Parent_annce_rsp.

      Other non-ZDO messages decoded into a ZCLFrame object and sent to Z_IncomingMessage().

      Incoming messages handling: Z_IncomingMessage~

      The starting point is Z_IncomingMessage() with a ZCLFrame object corresponding to the received Zigbee message.

      Details of Z_IncomingMessage():

      1. Log the raw message at LogLevel 3 (DEBUG)~

      2. Update the LQI for the device~

      3. Update the last_seen value~

      4. Dispatch according to message type~

      1. If ZCL_DEFAULT_RESPONSE, log and ignore (it's just the device acknowledge for the last message).

      2. If ZCL_REPORT_ATTRIBUTES, call parseReportAttributes(). This is the general case for sensor values (temperature...)

      3. If ZCL_READ_ATTRIBUTES_RESPONSE, call parseReadAttributesResponse(). This happens as a response to reading attributes, and the handling is similar to the attribute reporting (although the syntax of the message is slightly different).

      4. If ZCL_READ_ATTRIBUTES, call parseReadAttributes(). This happens rarely, typically when a device asks the coordinator for attributes like the local_time.

      5. If ZCL_READ_REPORTING_CONFIGURATION_RESPONSE, call parseReadConfigAttributes(). This is the response to ZbBindState command.

      6. If ZCL_CONFIGURE_REPORTING_RESPONSE, call parseConfigAttributes(). This is the response to ZbBind command.

      7. For cluster specific commands, call parseClusterSpecificCommand(). This is the general case when a command is received (for ex "Power":"toggle").

      All the previous commands add attributes to a local attr_list object. These attributes are have a key of eiher Cluster/Attribute type of String type.

      Note: it is important to keep attributes as Cluster/Attribute types so that we can later apply transformations on them.

      Note2: LinkQuality, Device, Name, Group and Endpoint are special values that do are not registered as actual attributes.

      Note3: BatteryPercentage is systematically added with the last known value to each attribute reporting.

      6. Apply transoformations to the attributes.~

      There are many transformations that are required because some device use proprietary values, or we need to compute new values out of the existing attributes.

      1. Reject Loopback If the message is sent from the coordinator to the coordinator itself, which can happen with broadcast message, it is discarded with DEBUG level log loopback message, ignoring.

      2. Generate synthetic attributes generateSyntheticAttributes(). This is mainly used for Xiaomi Aqara devices. Aqara uses cluster 0xFF01 and 0xFF02 to send structured messages. The good side is that it allows to send attributes from different clusters in a single message, whereas the ZCL standard would have required several messages. The bad side is that Aqara reuses the same attribute numbers for different value, and you need to know the device type to decode; which makes the whole process work only if the pairing process successfully got the ModelId. This is also used by Aqara Cube and Aqara vibration sensor to decode values.

      3. Remove invalid attributes removeInvalidAttributes() Any value out of normal range is removed, for example lumi.weather reporting a temperature below -100.0°C is removed.

      4. Apply synonyms applySynonymAttributes() Apply any synonym from the Zigbee plugin definitions on a per device basis. If matched, the synonym maps the attribute to a new cluster/attrid and applies a multiplier or divisor if required.

      5. Compute synthetic attributes computeSyntheticAttributes(). This is used to add computed attributes or fix some bugs in devices. Currently it computes the BatteryPercentage from the BatteryVoltage if the BatteryPercentage is not already present. It computes SeaPressure using the Tasmota Altitude setting. It fixes an Eurotronic bug in the encoding of Pi Heating Demand which is sent in the 0..255 range instead of 0..100 range. It fixes the IKEA Remote battery value which is half what it needs to be. It captures multipliers and divisors for AC Voltage/Current/Power (cluster 0x0B04) and stores them in the Z_Data.

      6. Generate callbacks and timers generateCallBacks(). This is used to register deferres callbacks. It is only used for Occypancy for now. Many PIR sensors report "Occupancy":1 but don't report the lack of occupancy. This function sets a timer to artificially generate "Occupancy":0 after a definite amount of time (defaults to 90 seconds).

      7. Post-process attributes Z_postProcessAttributes(). This function does the final transformation of attributes to their human readable format.

      First the endpoint is added as suffix if SetOption101 1 is set, if the source endpoint is not 1, and if the device is known to have more than one endpoint (check with ZbStatus2).

      Then the attribute is looked-up from the global Z_PostProcess table.

      If the attribute is mapped into Z_Data, the value is saved into its corresponding object. See ZbData. This allows for keeping last seen values for the Web UI.

      Similarly, some device specific values are recorded: ModelId, ManufacturerId, BatteryPercent.

      If the attribute as a multiplier value, the raw value is multiplied/divided by this value (ex: Temperature raw value is 1/100th of degrees, so the raw value is divided by 100).

      Finally the attribute name is replaced by its string value (ex: 0402/0000 is replace with Temperature).

      7. Publish the final message to MQTT or defer the message.~

      In the general case, attributes are not published immediately but kept in memory for a short period of time. This allows for debouncing of identical messages, and coalescing of values (Temperature, Pressure, Humidity) in a single MQTT message, even if there were received in 3 separate messages.

      The default timer is a compile time #define USE_ZIGBEE_COALESCE_ATTR_TIMER with a default value of 350 ms.

      Once a message is ready, it first checks if the value conflict with previously held values. If so, the previous message is immediately sent, and the new values are held in memory.

      Then is sets a timer to publish the values after the timer expired.

      \ No newline at end of file diff --git a/Zigbee/index.html b/Zigbee/index.html new file mode 100644 index 0000000000..7bee4d6c85 --- /dev/null +++ b/Zigbee/index.html @@ -0,0 +1,240 @@ + Zigbee - Tasmota
      Skip to content

      Zigbee

      Zigbee2Tasmota serves as a gateway for devices connected to a Zigbee wireless network to bridge their communications over to Wi-Fi

      If you are using ZHA or Zigbee2MQTT via Ser2Net, Tasmota is only passing bytes between the controller (HA or Z2M) and the Zigbee MCU. Please refer to their respective sites to ask any question. All of the below is dedicated to Zigbee2Tasmota mode.

      Some sections are marked as 'Advanced topic', you can skip them at first and read them only when you need to go deeper.

      Zigbee2Tasmota (Z2T) is a lightweight Zigbee gateway/bridge solution running on ESP8266/ESP8285 or ESP32 Wi-Fi chips. Hence it is easier to deploy wherever you want in your home. It was inspired by Zigbee2MQTT but it was written from scratch to make it fit into the resource constraints of an ESP82xx chip with just 80kB of RAM and only 1MB of flash memory.

      For quick reference about Zigbee commands see Zigbee Commands.

      Hardware~

      Zigbee2Tasmota (Z2T) supports a wide variety of Zigbee MCUs from Texas Instruments (CC253X, CC26x2, CC13x2) and from Silicon Labs (EFR32MG12/EFRMG21), and runs on ESP8266 or any variant of ESP32. Since ESP8266 has very limited resources, we strongly advise to prefer ESP32 based Zigbee gateways.

      Flashing and installation instructions for ESP32 based:

      Flashing and installation instructions for ESP8266 based:

      A complete list of Zigbee coordinators and Zigbee devices compatible with Z2T is in the Zigbee Device Compatibility Repository.

      Advanced topic: ZNP or EZSP~

      Z2T supports MCUs running either the ZNP or EZSP zigbee stack. They require different compilation options but from a user point of view the features are very similar.

      Z-Stack or ZNP (Zigbee Network Processor) is an open-source Zigbee stack from Texas Instruments and runs on TI MCUs. Ready to use firmwares are found on Koenkk's Github. Older CC2530 require special wiring or external devices for flashing, while more recent like Sonoff Zigbee Bridge Pro (CC2652P) can be flashed directly from Tasmota. Z2T supports ZNP version 2.6 (Z-Stack 1.3) and version 2.7 (Z-Stack 3.x).

      EZSP (EmberZNet Serial Protocol) is a commercial Zigbee stack from Silicon Labs. Sonoff/Eachen require encrypted signed firmwares, they can be found here. Z2T supports EZSP versions 6.7.6 or above (EZSP protocol v8), recommended is v6.7.9.

      Introduction~

      Before using Zigbee with Tasmota, you need to understand a few concepts. Here is a simplified comparison to the Wi-Fi equivalent (sort of).

      Zigbee concept Wi-Fi equivalent
      Zigbee coordinator
      The coordinator is responsible for selecting the channel, PanID, security policy, and stack profile for a network. Zigbee2Tasmota will act as a coordinator.
      You can have multiple coordinators as long as they have different PanIDs.
      Wi-Fi Access Point
      PanID
      (Personal Area Network IDentifier)
      This parameter is unique in a Zigbee network (16-bit integer, 0x0000–0x3FFF).
      At initial start a pseudo-random PanID is derived from the ESP MAC address.
      SSID (the Wi-Fi network name)
      ShortAddr
      Address of the device on the Zigbee network. This address is randomly assigned when the device first connects to the coordinator (16 bits integer, 0x0000–0xFFF7). The coordinator has address 0x0000.
      You need to track which device has which address or assign a "Friendly Name" to each new discovered device.
      IP address
      GroupAddr
      Group address of a collection of devices, it allows a single message to address multiple devices at once (16 bits integer, 0x0000–0xFFFF). For example a remote can turn on/off a group of lights. GroupAddr 0x0000 is not assigned.
      Multicast
      Endpoint
      The endpoint on the coordinator or on the Zigbee device the message is sent from/to. You can see endpoints as logical devices providing distinct features (8 bits integer, 1–240).
      TCP port
      IEEEAddr
      Device hardware address (64 bits). This is unique per device and factory assigned.
      MAC address
      Channel 11-26
      Default: 11 (See Zigbee-Wifi coexistence)
      Wi-Fi Channel
      Encryption Key
      128-bit encryption key.
      At initial start a pseudo-random Encryption key is derived from the ESP MAC address.
      Wi-Fi password
      Pairing
      By default the coordinator does not accept new devices unless put in pairing mode. When in pairing mode, it will accept pairing requests from any device within range.
      Default: pairing disabled
      WPS
      Cluster
      Clusters are a group of commands and attributes that define what a device can do. Think of clusters as a group of actions by function. A device can support multiple clusters to do a whole variety of tasks. The majority of clusters are defined by the ZigBee Alliance and listed in the ZigBee Cluster Library

      Configuration~

      Initial start~

      When you first start Z2T, pseudo-random parameters are derived from your device unique identifiers (MAC address...) for PanID and network encryption keys.

      Sonoff Zigbee Bridge Pro (ESP32 + CC2652P with ZNP)

      19:02:20.659 ZIG: rebooting ZNP device
      +19:02:22.960 ZbInput discarding byte 00
      +19:02:22.962 RSL: RESULT = {"ZbState":{"Status":1,"Message":"CCxxxx ZNP booted","RestartReason":"Power-up","MajorRel":2,"MinorRel":7}}
      +19:02:23.160 RSL: RESULT = {"ZbState":{"Status":50,"MajorRel":2,"MinorRel":7,"MaintRel":1,"Revision":20220219}}
      +19:02:23.311 RSL: RESULT = {"ZbState":{"Status":2,"Message":"Resetting configuration"}}
      +19:02:25.359 ZbInput discarding byte 00
      +19:02:25.911 RSL: RESULT = {"ZbState":{"Status":3,"Message":"Configured, starting coordinator"}}
      +19:02:30.062 RSL: RESULT = {"ZbState":{"Status":40,"NewState":9,"Message":"Started as coordinator"}}
      +19:02:30.162 RSL: RESULT = {"ZbState":{"Status":51,"IEEEAddr":"0x00124B0026B684E4","ShortAddr":"0x0000","DeviceType":7,"DeviceState":9,"NumAssocDevices":0}}
      +19:02:30.713 RSL: RESULT = {"ZbState":{"Status":0,"Message":"Started"}}
      +19:02:30.715 ZIG: Zigbee started
      +19:02:30.720 ZIG: No Zigbee device information
      +19:02:30.732 ZIG: Zigbee device data in File System (31 bytes)
      +

      Sonoff Zigbee Bridge (ESP8266 + EFR32 with EZSP)

      19:08:26.057 ZIG: Resetting EZSP device
      +19:08:27.261 RSL: RESULT = {"ZbState":{"Status":1,"Message":"EFR32 EZSP booted","RestartReason":"Power-on","Code":2}}
      +19:08:27.315 RSL: RESULT = {"ZbState":{"Status":55,"Version":"6.7.8.0","Protocol":8,"Stack":2}}
      +19:08:27.316 RSL: RESULT = {"ZbState":{"Status":3,"Message":"Configured, starting coordinator"}}
      +19:08:28.758 RSL: RESULT = {"ZbState":{"Status":56,"IEEEAddr":"0x680AE2FFFE6E103B","ShortAddr":"0x0000","DeviceType":1}}
      +19:08:28.759 RSL: RESULT = {"ZbState":{"Status":2,"Message":"Resetting configuration"}}
      +19:08:44.260 ZIG: Resetting EZSP device
      +19:08:45.463 RSL: RESULT = {"ZbState":{"Status":1,"Message":"EFR32 EZSP booted","RestartReason":"Power-on","Code":2}}
      +19:08:45.518 RSL: RESULT = {"ZbState":{"Status":55,"Version":"6.7.8.0","Protocol":8,"Stack":2}}
      +19:08:45.520 RSL: RESULT = {"ZbState":{"Status":3,"Message":"Configured, starting coordinator"}}
      +19:08:46.822 ZIG: Factory reset EZSP device
      +19:08:47.053 ZIG: Subscribe to group 0 'ZbListen0 0'
      +19:08:47.108 RSL: RESULT = {"ZbState":{"Status":0,"Message":"Started"}}
      +19:08:47.108 ZIG: Zigbee started
      +19:08:47.138 ZIG: No Zigbee device information
      +19:08:47.145 ZIG: No Zigbee device data
      +

      When you further restart, logs are slightly shorter (less lines) since the MCU is already configured.

      Customize Zigbee configuration~

      At initial start, Tasmota generates pseudo-random Zigbee parameters. They should work out of the box but you also may want to choose different values:

      Use the command ZbConfig to dump the current configuration. For example it might return {"ZbConfig":{"Channel":11,"PanID":"0x1A63","ExtPanID":"0xCCCCCCCCCCCCCCCC","KeyL":"0x0F0D0B0907050301","KeyH":"0x0D0C0A0806040200"}}

      To apply a new configuration, populate the fields you want to change in a single-level JSON:

      ZbConfig {"Channel":11,"PanID":"0x1A63","ExtPanID":"0xCCCCCCCCCCCCCCCC","KeyL":"0x0F0D0B0907050301","KeyH":"0x0D0C0A0806040200"}

      Changing any parameter with ZbConfig requires to re-pair all devices.

      Parameter Description
      Channel 11-26 Zigbee radio channel, see above
      PanID 0x0001-0x3FFF unique Zigbee network identifier. You should not have two coordinators using the same PanID, or bad things happen.
      If your coordinator fails to start, try changing the PanID as it may conflict with one already in use.
      ExtPanID This is a 64 bit unique identifier for the network. It is not used much in Z2T but needs to be unique.
      KeyL KeyH This is the 128 bit network encryption key split into High and Low 64 bit parts. Do not reveal this key or anyone can decrypt your Zigbee traffic.
      TxRadio On some devices you can set the radio power in dBm. You generally don't need to change it.

      Advanced topic: GPIOs~

      You will usually find a ready to use template in the Zigbee Device Compatibility Repository

      Below are the details of the GPIO configuration needed by Zigbee:

      GPIO Description
      Zigbee Tx and Zigbee Rx GPIOs used for serial communication between Tasmota and the MCU (needed for both ZNP and EZSP). Communication is always 115200 bps 8N1.
      Zigbee Rst 1 (optional) defines the hardware Reset for the Zigbee MCU. If not defined, Z2T will fallback to sending a soft-reset to the MCU using a special serial sequence (which may fail on rare occasions).
      Zigbee Rst 2 (optional) defines a special pin used to put the MCU in bootloader and flashing mode when the hardware reset is pulled down. This is used both by EFR32 and CC2652P based devices. It allows to flash the MCU directly from Tasmota.
      LedLink or LedLink_i (optional) when defined, this led keeps its normal Wifi/MQTT status indicator (blinking) and adds a glowing light when Permit Join is active (i.e. new devices are allowed to pair).
      Led 1 or Led_i 1 (optional) when defined, used as an indicator of traffic between Tasmota and the MCU, which generally means Zigbee traffic is sent or received.

      Advanced topic: Hardware or Software serial~

      On ESP32 serial is always handled by hardware so you don't need to bother.

      On ESP8266 using the hardware serial is preferred. To do so, you need to use GPIOs 13/15 for Zigbee Rx/Tx and set SerialLog 0. By doing so, Z2T steals the hardware UART from the serial console and uses it for communicating with the MCU. Otherwise Z2T uses Software Serial which requires compiling at 160MHz and might be unreliable on very rare occasions.

      Commands~

      For a list of available commands see Zigbee Commands.

      Quick start~

      In this section, we'll give a quick overview of 2 devices:

      Definition File~

      From the start, Z2T design was to stick to a low-level view and provide higher level (named) attributes only for a limited set of mostly seen attributes. This raised difficulties and frustration for users with specific devices that use rare attributes, or devices that use non-standard attributes (like Tuya zigbee devices).

      We are now providing a Zigbee Device plugin mechanisms, using simple text files. These files specify mapping on a per-device type basis. The goal is to fill most of the gap with Zigbee2MQTT (provided that you write the device plugin files). The lightweight nature of plugins permits to load only the plugins required by the devices used, and does not require a sowftare update for new devices.

      How does it work?~

      You simply copy device plugin files (externsion *.zb) in the file system and they are automatically loaded at start.

      You can dynamically load new files with ZbLoad <file>.zb or unload definitions with ZbUnload <file>.zb. When you reload a file with the same name, it is first unloaded.

      At Zigbee start, all files with *.zb suffix are loaded into memory. Be careful of not saturating memory, especially on ESP8266.

      Zigbee device plugin format~

      Zigbee device plugin have the following format:

      • starts with #Z2Tv1 on the first line
      • # is a marker for comments, and everything from # to end of line is ignored
      • rest of the file is of form device matcher followed by attribute definitions or attribute synonyms

      device matchers~

      • composed of one or more lines defining the modelId and manufacturerId. If a field is empty, it matches all values
      • :<modelId>,<manufacturerId>
      • example: :TS0601,_TZE200_sh1btabb for GiEX water valve

      attribute matcher specifies a cluster/attribute/type tuple and matches an attribute name~

      • <cluster 4 hex>/<attribute 4 hex> or <cluster 4 hex>/attribute 4 hex>%<type 2 hex>
      • Ex: EF00/0365,IrrigationStartTime (Tuya cluster EF00 does not need an explicit type)
      • Ex: 0006/4001%bool,OnTime

      attribute synonyms specifies that a received attribute is a synonym for another attribute~

      • <cluster 4 hex>/<attribute 4 hex>=<new_cluster 4 hex>/<new_attribute 4 hex>,<multiplier>
      • Ex: EF00/026C=0001/0021,2 converts any EFOO/026C attribute received to 0001/0021 (BatteryPercentage) and multiplies by 2 to convert to ZCL standard.

      Multiplier is 8 bit int (-128..127). If 0 or 1, the value is unchanged. Otherwise the value is converted to float and is multiplied by multiplier if positive, or divided by -multiplier if negative.

      I.e. multiplier=10 means multiply by 10, multiplier=-5 means divide by 5

      Sonoff SNZB-02 Sensor~

      SNZB-02

      Put Z2T in pairing mode (command ZbPermitJoin 1 or via WebUI) and keep the button of the sensor pressed for 5 seconds. Wait 20 seconds, you should see something similar to this in the logs:

      17:07:53.015 RSL: RESULT = {"ZbState":{"Status":34,"IEEEAddr":"0x00124B001F841E41","ShortAddr":"0x2916","ParentNetwork":"0x0000"}}
      +17:07:53.465 RSL: RESULT = {"ZbState":{"Status":30,"IEEEAddr":"0x00124B001F841E41","ShortAddr":"0x2916","PowerSource":false,"ReceiveWhenIdle":false,"Security":false}}
      +17:07:54.565 RSL: RESULT = {"ZbState":{"Status":32,"ActiveEndpoints":["0x01"]}}
      +17:07:55.037 ZIG: Zigbee Devices Data saved in File System (18 bytes)
      +17:07:55.168 RSL: SENSOR = {"ZbReceived":{"0x2916":{"Device":"0x2916","ModelId":"TH01","Manufacturer":"eWeLink","Endpoint":1,"LinkQuality":120}}}
      +17:07:56.667 RSL: RESULT = {"ZbState":{"Status":33,"Device":"0x2916","Endpoint":"0x01","ProfileId":"0x0104","DeviceId":"0x0302","DeviceVersion":0,"InClusters":["0x0000","0x0003","0x0402","0x0405","0x0001"],"OutClusters":["0x0003"]}}
      +17:07:57.241 ZIG: Zigbee Devices Data saved in File System (29 bytes)
      +17:07:58.667 ZIG: auto-bind `ZbBind {"Device":"0x2916","Endpoint":1,"Cluster":"0x0001"}`
      +17:07:59.217 RSL: RESULT = {"ZbBind":{"Device":"0x2916","Status":0,"StatusMessage":"SUCCESS"}}
      +17:08:00.717 ZIG: auto-bind `ZbBind {"Device":"0x2916","Endpoint":1,"Cluster":"0x0402"}`
      +17:08:01.018 RSL: RESULT = {"ZbBind":{"Device":"0x2916","Status":0,"StatusMessage":"SUCCESS"}}
      +17:08:02.669 ZIG: auto-bind `ZbBind {"Device":"0x2916","Endpoint":1,"Cluster":"0x0405"}`
      +17:08:03.618 RSL: RESULT = {"ZbBind":{"Device":"0x2916","Status":0,"StatusMessage":"SUCCESS"}}
      +17:08:04.670 ZIG: auto-bind `ZbSend {"Device":"0x2916","Config":{"BatteryVoltage":{"MinInterval":3600,"MaxInterval":14400,"ReportableChange":0.2},"BatteryPercentage":{"MinInterval":3600,"MaxInterval":14400,"ReportableChange":5}}}`
      +17:08:05.271 RSL: SENSOR = {"ZbReceived":{"0x2916":{"Device":"0x2916","ConfigResponse":{},"Endpoint":1,"LinkQuality":123}}}
      +17:08:06.670 ZIG: auto-bind `ZbSend {"Device":"0x2916","Config":{"Temperature":{"MinInterval":30,"MaxInterval":3600,"ReportableChange":0.5}}}`
      +17:08:06.871 RSL: SENSOR = {"ZbReceived":{"0x2916":{"Device":"0x2916","ConfigResponse":{},"Endpoint":1,"LinkQuality":120}}}
      +17:08:08.670 ZIG: auto-bind `ZbSend {"Device":"0x2916","Config":{"Humidity":{"MinInterval":30,"MaxInterval":3600,"ReportableChange":1}}}`
      +17:08:09.421 RSL: SENSOR = {"ZbReceived":{"0x2916":{"Device":"0x2916","ConfigResponse":{},"Endpoint":1,"LinkQuality":120}}}
      +17:08:14.221 RSL: SENSOR = {"ZbReceived":{"0x2916":{"Device":"0x2916","Temperature":25.72,"Humidity":47.73,"Endpoint":1,"LinkQuality":116}}}
      +

      These logs are quite rich and we'll dive into the details later. Basically it says that the device paired successfully and is configured.

      Next step is setting a friendly name with zbname 0x2916,SNZB-02

      17:09:27.294 CMD: zbname 0x2916,SNZB-02
      +17:09:27.297 RSL: RESULT = {"0x2916":{"Name":"SNZB-02"}}
      +17:09:29.375 ZIG: Zigbee Devices Data saved in File System (37 bytes)
      +

      The device will regularly report readings like this:

      17:09:44.351 RSL: SENSOR = {"ZbReceived":{"0x2916":{"Device":"0x2916","Name":"SNZB-02","Temperature":26.27,"Endpoint":1,"LinkQuality":105}}}
      +

      Information is also displayed in the WebUI.

      SNZB-02

      When you hover the arrow on the name, you get additional information like short-address and manufacturer.

      SNZB-02-info

      When you hover over the battery icon, you get a more precise reading.

      SNZB-02-batt

      BlitzWolf SHP15 Plug~

      SNZB-02

      Put Z2T in pairing mode (command ZbPermitJoin 1 or via WebUI) and keep the button of the sensor pressed for 5 seconds. Wait 20 seconds, you should see something similar to this in the logs:

      16:17:40.804 RSL: RESULT = {"ZbState":{"Status":34,"IEEEAddr":"0x842E14FFFE13A51E","ShortAddr":"0x7120","ParentNetwork":"0x0000"}}
      +16:17:40.854 RSL: RESULT = {"ZbState":{"Status":30,"IEEEAddr":"0x842E14FFFE13A51E","ShortAddr":"0x7120","PowerSource":true,"ReceiveWhenIdle":true,"Security":false}}
      +16:17:41.003 RSL: RESULT = {"ZbState":{"Status":32,"ActiveEndpoints":["0x01"]}}
      +16:17:41.408 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","ModelId":"TS0121","Manufacturer":"_TZ3000_g5xawfcq","Endpoint":1,"LinkQuality":229}}}
      +16:17:42.655 RSL: RESULT = {"ZbState":{"Status":33,"Device":"0x7120","Endpoint":"0x01","ProfileId":"0x0104","DeviceId":"0x0051","DeviceVersion":1,"InClusters":["0x0000","0x0004","0x0005","0x0006","0x0702","0x0B04"],"OutClusters":["0x0019","0x000A"]}}
      +16:17:43.442 ZIG: Zigbee Devices Data saved in File System (40 bytes)
      +16:17:44.516 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","Power":1,"0006/4001":0,"0006/4002":0,"0006/8001":1,"0006/8002":2,"Endpoint":1,"LinkQuality":229}}}
      +16:17:44.664 ZIG: auto-bind `ZbBind {"Device":"0x7120","Endpoint":1,"Cluster":"0x0006"}`
      +16:17:44.863 RSL: RESULT = {"ZbBind":{"Device":"0x7120","Status":0,"StatusMessage":"SUCCESS"}}
      +16:17:46.466 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","EnergyTotal":"0x000000000000","Endpoint":1,"LinkQuality":229}}}
      +16:17:46.664 ZIG: auto-bind `ZbSend {"Device":"0x7120","Config":{"Power":{"MinInterval":1,"MaxInterval":3600}}}`
      +16:17:46.916 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","ConfigResponse":{},"Endpoint":1,"LinkQuality":229}}}
      +16:17:47.815 ZIG: Auto-responder: ZbSend {"Device":"0x7120","Cluster":"0x000A","Endpoint":1,"Response":"000A/0007":711476267}
      +16:17:47.819 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","Cluster":10,"Read":[7],"ReadNames":{"LocalTime":true},"Endpoint":1,"LinkQuality":229}}}
      +

      Again lots of lines we'll not explore for now.

      You can turn on the plug:

      zbsend {"device":"SHP15","send":{"power":true}}

      16:24:24.208 RSL: RESULT = {"ZbSend":"Done"}
      +16:24:25.005 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","Name":"SHP15","Power":1,"Endpoint":1,"LinkQuality":229}}}
      +

      You can turn off the plug: (you can use "power":0 or "power":false)

      zbsend {"device":"SHP15","send":{"power":0}}

      16:24:01.456 RSL: RESULT = {"ZbSend":"Done"}
      +16:24:02.252 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","Name":"SHP15","Power":0,"Endpoint":1,"LinkQuality":229}}}
      +

      You can read the current state: (you can use "power":1 or "power":true)

      zbsend {"device":"SHP15","read":{"power":true}}

      16:24:45.265 RSL: RESULT = {"ZbSend":"Done"}
      +16:24:45.815 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","Name":"SHP15","Power":1,"Endpoint":1,"LinkQuality":229}}}
      +

      You can read the current power: (here 0W)

      zbsend {"device":"SHP15","read":{"activepower":true}}

      16:29:17.309 RSL: RESULT = {"ZbSend":"Done"}
      +16:29:17.860 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","Name":"SHP15","ActivePower":0,"Endpoint":1,"LinkQuality":229}}}
      +

      When you directly turn on or off the plug with its button, it also spontaneously reports the changes:

      16:29:45.660 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","Name":"SHP15","Power":0,"Endpoint":1,"LinkQuality":218}}}
      +16:29:52.460 RSL: SENSOR = {"ZbReceived":{"0x7120":{"Device":"0x7120","Name":"SHP15","Power":1,"Endpoint":1,"LinkQuality":218}}}
      +

      The WebUI is also reporting the last known values:

      SHP15

      Advanced topic: logs when pairing~

      This is an example of a pairing process for the Aqara Temperature & Humidity Sensor. To pair this sensor, issue ZbPermitJoin 1 and then press and hold the reset button for 5 seconds. The sensor LED will flash several times and you will see log entries in the console, especially this one:

      MQT: tele/%topic%/SENSOR = {"ZbState":{"Status":30,"IEEEAddr":"0x00158D00036B50AE","ShortAddr":"0x8F20","PowerSource":false,"ReceiveWhenIdle":false,"Security":false}}
      +

      Message with "Status":30 shows some characteristics of the device:

      Field name Value
      Status 30 indicates a device connect or reconnect. This is the opportunity to match IEEEAddress and short address
      IEEEAddr Long unique address (64 bits) of the device - factory set
      ShortAddr Short address (16 bits) randomly assigned to the device on this Zigbee network
      PowerSource true = the device is connected to a power source
      false = the device runs on battery
      ReceiveWhenIdle true = the device can receive commands when idle
      false = the device is not listening. Commands should be sent when the device reconnects and is idle
      Security Security capability (meaning unknown, to be determined)

      Advanced topic: Zigbee plugin Definition File~

      From the start, Z2T design was to stick to a low-level view and provide higher level (named) attributes only for a limited set of mostly seen attributes. This raised difficulties and frustration for users with specific devices that use rare attributes, or devices that use non-standard attributes (like Tuya zigbee devices).

      We are now providing a (Zigbee Device plugin)[#advanced-topic-zigbee-device-plugin] mechanisms, using simple text files. These files specify mapping on a per-device type basis. The goal is to fill most of the gap with Zigbee2MQTT (provided that you write the device plugin files). The lightweight nature of plugins permits to load only the plugins required by the devices used, and does not require a software update for new devices.

      How does it work?~

      You simply copy device plugin files (externsion *.zb) in the file system and they are automatically loaded at start.

      During troubleshooting, you can dynamically load new files with ZbLoad <file>.zb or unload definitions with ZbUnload <file>.zb. When you reload a file with the same name, it is first unloaded.

      At Zigbee start, all files with *.zb suffix are loaded into memory. Be careful of not saturating memory, especially on ESP8266.

      Pairing Devices~

      When you create a new Zigbee network, it contains no devices except the coordinator (your Zigbee gateway). The first step is to add devices to the network, which is called pairing.

      By default, and for security reasons, the Zigbee coordinator does not automatically accept new devices. To pair new devices, use ZbPermitJoin 1 or click Zigbee Permit Join in the WebUI to allow accepting new devices for the next 60 seconds. Then put your Zigbee device pairing mode. This is usually accomplished by pressing the button on the device for 5 seconds or more.

      ZbPermitJoin 1

      CMD: ZbPermitJoin 1
      +MQT: stat/%topic%/RESULT = {"ZbPermitJoin":"Done"}
      +MQT: tele/%topic%/RESULT = {"ZbState":{"Status":21,"Message":"Enable Pairing mode for 60 seconds"}}
      +

      60 seconds later:

      MQT: tele/%topic%/RESULT = {"ZbState":{"Status":20,"Message":"Disable Pairing mode"}}
      +

      After the device has successfully paired it will be shown in the WebUI with its short address and its link quality number (LQI). When it is a battery powered device, the battery percentage will be displayed as soon as it is received from the device.

      SNZB-02

      Devices will show their friendly name once you set it.

      Setting Friendly Name~

      Instead of a short address like 0x8F20 you can assign an easy to remember friendly name such as Bedroom_Sensor.

      See ZbName command for all options.

      Xiaomi Aqara Cube with address 0x128F

      MQT: tele/%topic%/RESULT = {"ZbReceived":{"0x128F":{"AqaraVibrationMode":"tilt","AqaraVibrationsOrAngle":162,"AqaraAccelerometer":[-690,2,138],"AqaraAngles":[-78,0,11],"LinkQuality":158}}}
      +

      Setting its friendly name to Vibration_sensor:

      ZbName 0x128F,Vibration_sensor
      +CMD: ZbName 0x128F,Vibration_sensor
      +MQT: stat/%topic%/RESULT = {"0x128F":{"Name":"Vibration_sensor"}}
      +
      +(10 seconds later)
      +ZIG: Zigbee Devices Data store in Flash (0x402FF800 - 270 bytes)
      +

      Now the sensor readings includes the friendly name:

      MQT: tele/%topic%/RESULT = {"ZbReceived":{"0x128F":{"Name":"Vibration_sensor","AqaraVibrationMode":"tilt","AqaraVibrationsOrAngle":171,"AqaraAccelerometer":[-691,12,130],"AqaraAngles":[-78,1,11],"LinkQuality":153}}}
      +

      If you set SetOption83 1 sensor readings will use the friendly name as JSON key, short address is added as Device:

      MQT: tele/%topic%/RESULT = {"ZbReceived":{"Vibration_sensor":{"Device":"0x128F","AqaraVibrationMode":"tilt","AqaraVibrationsOrAngle":171,"AqaraAccelerometer":[-691,8,136],"AqaraAngles":[-78,1,11],"LinkQuality":153}}}
      +

      Removing Devices~

      A Zigbee device will continue to connect to a coordinator unless you ask it to "leave" or if you change the network configuration (change of PanID or network key - which means losing ALL devices).

      To ask a device to leave the network, use command ZbLeave <device> or ZbLeave <friendlyname>. This sends a message to the device, which needs to be awake to process it. For battery powered devices, you need to wake them up when sending this command. Unfortunately there is no confirmation message sent back. Note: even if the device left the network, it is still registered in Z2T and continues to appear on the UI. To remove it from the list, use ZbForget below.

      To remove a device from the Zigbee2Tasmota list of devices and from the WebUI, use the command ZbForget <device> or ZbForget <friendlyname>. If the device is still connected to the network, it will pop up again later. I.e. ZbForget does not remove a device from the network; else use ZbLeave above.

      Advanced topic: Device Information~

      You can get a quick list of Zigbee devices with the command ZbStatus.

      Example:

      18:30:58.972 CMD: ZbStatus
      +18:30:58.980 MQT: stat/xxx/RESULT = {"ZbStatus1":[{"Device":"0xECD0","Name":"IKEA_Bulb"},{"Device":"0x8959","Name":"Plug"}]}
      +

      You can use the command ZbInfo to display all information, endpoints and last known values for the main attributes. There are variants in the commands arguments:

      • ZbInfo (no arg): list all known device one after the other
      • ZbInfo 0xECD0: show information of a device by short address
      • Zbinfo IKEA_Bulb: show information of a device by friendly name
      • Zbinfo 0x90FD9FFFFE03B051: show information of a device by long address (IEEE address)
      • ZbInfo 1, ZbInfo 2... iterate through devices in sequence

      ZbInfo does not query the device, it only shows the last known state of the device from Tasmota memory

      Example:

      18:38:51.523 CMD: ZbInfo
      +18:38:51.532 MQT: tele/xxx/SENSOR = {"ZbInfo":{"0xECD0":{"Device":"0xECD0","Name":"IKEA_Bulb","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":[1],"Config":["O01","L01.2"],"Power":1,"Dimmer":160,"X":30138,"Y":26909,"CT":350,"ColorMode":2,"RGB":"FFC773","RGBb":"A17E49","Reachable":true,"LastSeen":353,"LastSeenEpoch":1658349178,"LinkQuality":79}}}
      +18:38:51.570 MQT: tele/xxxx/SENSOR = {"ZbInfo":{"0x8959":{"Device":"0x8959","Name":"Plug","IEEEAddr":"0x7CB03EAA0A0292DD","ModelId":"Plug 01","Manufacturer":"OSRAM","Endpoints":[3],"Config":["L03.0","O03"],"Dimmer":254,"Power":0,"Reachable":false,"LastSeen":16607299,"LastSeenEpoch":1641742232,"LinkQuality":147}}}
      +

      (formatted for readability)

      18:38:51.532 MQT: tele/xxx/SENSOR = 
      +{
      +    "ZbInfo": {
      +        "0xECD0": {
      +            "Device": "0xECD0",
      +            "Name": "IKEA_Bulb",
      +            "IEEEAddr": "0x90FD9FFFFE03B051",
      +            "ModelId": "TRADFRI bulb E27 WS opal 980lm",
      +            "Manufacturer": "IKEA of Sweden",
      +            "Endpoints": [1],
      +            "Config": ["O01", "L01.2"],
      +            "Power": 1,
      +            "Dimmer": 160,
      +            "X": 30138,
      +            "Y": 26909,
      +            "CT": 350,
      +            "ColorMode": 2,
      +            "RGB": "FFC773",
      +            "RGBb": "A17E49",
      +            "Reachable": true,
      +            "LastSeen": 353,
      +            "LastSeenEpoch": 1658349178,
      +            "LinkQuality": 79
      +        }
      +    }
      +}
      +18:38:51.570 MQT: tele/xxxx/SENSOR = 
      +{
      +    "ZbInfo": {
      +        "0x8959": {
      +            "Device": "0x8959",
      +            "Name": "Plug",
      +            "IEEEAddr": "0x7CB03EAA0A0292DD",
      +            "ModelId": "Plug 01",
      +            "Manufacturer": "OSRAM",
      +            "Endpoints": [3],
      +            "Config": ["L03.0", "O03"],
      +            "Dimmer": 254,
      +            "Power": 0,
      +            "Reachable": false,
      +            "LastSeen": 145,
      +            "LastSeenEpoch": 1641742232,
      +            "LinkQuality": 147
      +        }
      +    }
      +}
      +

      Most common attributes:

      Attribute Description
      Device Zigbee device short address
      Name Friendly name
      IEEEAddr Zigbee device long address (does not change after new pairing)
      ModelID Zigbee Model name as configured by manufacturer (cannot be changed)
      Manufacturer Manufacturer name
      Endpoints List of endpoints
      Config (used internally)
      (attributes) attributes tracked by Z2T
      LastSeen Number of seconds since the last message was received
      LastSeenEpoch Timestamp when the last message was received
      LinkQuality Radio power of the last message received

      Advanced topic: Sending sensor values to separated MQTT topics~

      It is possible to publish the sensor values to their own MQTT topic. For this functionality the following rule can be applied in the console:

      Rule<x>
      +  on zbreceived#<zigbee_id>#<zigbee_sensorname> do publish home/zigbee/<zigbee_name>/<sensorname> %value% endon
      +
      +Rule<x> 1
      +

      For example:

      Rule1
      +  on zbreceived#0xAA7C#humidity do publish home/zigbee/office/humidity %value% endon
      +  on zbreceived#0xAA7C#temperature do publish home/zigbee/office/temperature %value% endon
      +
      +Rule1 1
      +

      If retained values are preferred use publish2 instead of publish.

      Understanding Zigbee messages~

      There are 2 main types of Zigbee messages, commands and reading/writing attributes.

      For example, you can send a command "Power":1 to turn a bulb on or "Power":0 to turn it off.

      Simultaneously you can read the "Power" attribute to know the state of the bulb.

      Some attributes are writable, but this corresponds to a change of configuration of the device. You generally can't change the status of a device writing to attributes, you need to use commands instead.

      Internally, Zigbee uses low-level identifiers and Z2T provides human readable versions for the main attributes and commands. In the example above, although the command and the attribute have the same name "Power", they have different low-lever identifiers.

      Operations on attributes and commands~

      Below are the possible Zigbee messages (we consider here messages between the coordinator and the Zigbee device):

      • Read attribute(s): send a 'read-attribute' message to the Zigbee device, the device then responds with the value of the attributes it supports. Ex: read the current brightness of a bulb or a sensor. ZbSend {"Device":"<device>", "Read":{...}}

      • Write attribute(s): send a 'write-attribute' message to the Zigbee device with a value, the device confirms or sends an error. Ex: change the sensitivity of an illuminance sensor. ZbSend {"Device":"<device>", "Write":{...}}

      • Report attribute(s): Zigbee device may spontaneously report attributes without polling. This happens typically with sensors. New attribute values are sent after a certain time or when the value changes above a threshold.

      • Send a command: send a command to a Zigbee device, ex: turn on a bulb, change its color... ZbSend {"Device":"<device>", "Send":{...}}

      • Receive a command: Zigbee device may send commands to the coordinator (less frequent). Ex: an alarm sensor sends an 'Intruder Alert' command to the coordinator.

      When a command is sent or an attribute is written to a device, the device may or may not acknowledge. However it will always report an error if the message is malformed or if some attributes/commands are not supported.

      Sleeping devices~

      Devices connected to mains (smart plugs, smart bulbs...) are always reachable and can accept Zigbee messages at any time.

      Devices that are powered by batteries are not always reachable. Most of the time they are in sleep mode and not reachable. They regularly connect back to the coordinator to send new values and get messages (ex: once per hour). When you need to send messages to battery-powered devices, you must first wake them up, for example pressing on a button. The device may stay awake for only a couple of seconds, so you must send the message just before or just after pressing the button on the device.

      Advanced topic: Endpoints and Clusters~

      An endpoint supports different functions separated in clusters, and a device can have multiple endpoints to do different things. To simplify, think of your Zigbee device as a normal Tasmota device with a Zigbee radio instead of Wi-Fi. Each endpoint is akin to a GPIO that has connected Components or Clusters, in Zigbee terms.

      Cluster definitions in relation to their endpoint are determined by Zigbee Alliance. Not all manufacturers followed the proposed allocations but in general it is a cornerstone document.

      Z2T will automatically take the first endpoint in the list which works most of the time. You normally don't need to specify the endpoint number. In rare cases, you can force a specific endpoint.

      Reading Sensors~

      Most sensors will publish their readings regularly or once a significant change has happened: temperature, pressure, humidity, presence, illuminance...

      Sensor messages are published via MQTT when they are received from the Zigbee device. Similar to Zigbee2MQTT, Z2T tries to group and debounce sensor values when they are received within a 350ms window (can be change with USE_ZIGBEE_COALESCE_ATTR_TIMER compile option).

      Aqara Temperature & Humidity Sensor

      This sensor monitors humidity, temperature, and air pressure. Its Zigbee model ID is lumi.weather.

      This device publishes sensor values roughly every hour or when a change occurs. You can also force an update pressing the device's button. It sends two kinds of messages, either 3x standard Zigbee messages, or a single proprietary message containing all sensor values.

      0x8F20 is the ShortAddress of the sensor, and its name is Kitchen if you used ZbName 0x8F20,Kithchen.

      MQT: tele/%topic%/SENSOR ={"ZbReceived": {"0x8F20": {"Name": "Kitchen", "Voltage": 2.995, "Battery": 98, "Temperature": 21.01, "Humidity": 53.68, "Pressure": 1004.04, "PressureUnit": "hPa", "Endpoint": 1, "LinkQuality": 88}}
      +

      or prefixed by name if you set SetOption83 1

      MQT: tele/%topic%/SENSOR ={"ZbReceived": {"Kitchen": {"Device": "0x8F20", "Voltage": 2.995, "Battery": 98, "Temperature": 21.01, "Humidity": 53.68, "Pressure": 1004.04, "PressureUnit": "hPa", "Endpoint": 1, "LinkQuality": 88}}
      +

      Topic is device specific, to allow more effective retained messages, if you set SetOption89 1

      MQT: tele/%topic%/8F20/SENSOR ={"ZbReceived": {"Kitchen": {"Device": "0x8F20", "Voltage": 2.995, "Battery": 98, "Temperature": 21.01, "Humidity": 53.68, "Pressure": 1004.04, "PressureUnit": "hPa", "Endpoint": 1, "LinkQuality": 88}}
      +

      Supported values:

      Field name Value
      LinkQuality Strength of the Zigbee signal, between 1 and 254 (integer). See this ZigBee and WiFi Coexistence
      Humidity Humidity in percentage (float)
      Pressure and PressureUnit Atmospheric pressure (float) and unit (string)
      Currently only hPa (A.K.A. mbar) is supported
      Temperature Temperature in Celsius (float)
      Voltage Battery voltage (float)
      Battery Battery charge in percentage (integer)
      ModelId Model name of the Zigbee device (string)
      Ex: lumi.weather
      ScaledValue and Scale Give the raw measure and the scale correction as 10^scale
      And many more...

      If a value is not decoded, it will appear as "<cluster>_<attr>":<value> where <cluster> is the Zigbee ZCL Cluster of the attribute (family), <attr> is the attribute number and <value> its published value.

      Example

      "0402_0000":2240 is attribute 0x0000 from cluster 0x0402, which is the temperature in hundredth of °C. It is automatically converted to "Temperature":22.40.

      Sending Device Commands~

      You can send commands to a device or groups of devices similar to a normal Tasmota command. For example to turn on a light or switch off a plug.

      Here is a list of supported commands, see below how to send any unlisted command.

      Command Parameters Cluster number
      Power 1 or true or "true" or "on": On
      0 or false or "false" or "off": Off
      2 or "toggle": Toggle
      0x0006
      Dimmer 0..254: Dimmer value
      255 is normally considered as invalid, and may be converted to 254
      0x0008
      DimmerUp : no parameter. Increases dimmer by 10% 0x0008
      DimmerDown : no parameter. Decreases dimmer by 10% 0x0008
      DimmerStop : no parameter. Stops any running increase of decrease of dimmer. 0x0008
      ResetAlarm <alarmcode>,<clusterid>: (to be documented later) 0x0009
      ResetAllAlarms : no parameter, (to be documented later) 0x0009
      Hue 0..254: change Hue value 0x0300
      Sat 0..254: change Sat value 0x0300
      HueSat 0..254,0..254: change both Hue and Sat values 0x0300
      Color 0..65534,0..65534: change the color using [x,y] coordinates 0x0300
      CT 0..65534: change the white color-temperature in mireds 0x0300
      Shutter 0..254: send any Shutter command (prefer the commands below) 0x0102
      ShutterOpen : no parameter, open shutter 0x0102
      ShutterClose : no parameter, close shutter 0x0102
      ShutterStop : no parameter, stop shutter movement 0x0102
      ShutterLift 0..100: move shutter to a specific position in percent
      0%=open, 100%=closed
      0x0102
      ShutterTilt 0..100: move the shutter to the specific tilt position in percent 0x0102

      The format of the command is following:

      ZbSend {"Device":"<device>","Send":{"<sendcmd>":<sendparam>}} where
      <device>identifies the target and can be a shortaddr 0x1234, a longaddr 0x1234567812345678 or a friendly name Kitchen.
      "<sendcmd>":<sendparam> is the command and its parameters from the table.

      If the device has been correctly paired and its endpoints recorded by Z2T, you shouldn't need to specify a target endpoint. You can use an option "endpoint":<endpoint> parameter if Z2T can't find the correct endpoint or if you want to change from the default endpoint.

      MQTT command example

      Topic = cmnd/ZigbeeGateway/ZbSend
      Payload = {"Device":"0x1234","Send":{"Power":0}} or {"Device":"0x1234","Write":{"Power":0}}

      Low-level Commands~

      There is a special syntax if you want to send arbitrary commands: "Send":"<send_bytes>" where <send_bytes> has the following syntax:

      "<cluster>_<cmd>/<bytes>": send a non-cluster specific command for cluster id <cluster>, command id <cmd> and payload <bytes>.

      Example

      ZbSend {"Device":"0x1234","Send":"0000_00/0500"} Send a Read command (0x00) to the general cluster (0x0000) for attribute ManufId (0x0005). Note: all values are little-endian.

      Or use '!' instead of '_' to specify cluster-specific commands:

      "<cluster>!<cmd>/<bytes>": send a cluster specific command for cluster id <cluster>, command id <cmd> and payload <bytes>.

      Example

      ZbSend {"Device":"0x1234","Send":"0008!04/800A00"} Send a Dimmer command (0x04) from Level Control cluster (0x0008) with payload being: Dimmer value 0x80, and transition time of 1 second (0x000A = 10 tenths of seconds).

      Of course the latter example could be simply: ZbSend {"Device":"0x1234","Send":{"Dimmer":"0x80"}

      Examples~

      Plug~

      ZbSend { "device":"0x4773", "send":{"Power":"On"} }
      +ZbSend { "device":"0x4773", "send":{"Power":1} }
      +ZbSend { "device":"0x4773", "send":{"Power":false} }
      +ZbSend { "device":"0x4773", "send":{"Power":"Toggle"} }
      +

      Read the On/Off status: (all three commands below are synonyms)

      ZbSend { "device":"0x4773", "endpoint":"0x03", "cluster":"0x0006", "read":"0x0000" }
      +ZbSend { "device":"0x4773", "endpoint":"0x03", "cluster":"0x0006", "read":["0x0000"] }
      +ZbSend { "device":"0x4773", "endpoint":3, "cluster":6, "read":0 }
      +
      MQT: tele/tasmota/SENSOR = {"ZbReceived":{"0x4773":{"Power":true,"LinkQuality":52}}}
      +

      Bulb~

      ZbSend { "device":"0x3D82", "send":{"Power":"Off"} }
      +ZbSend { "device":"0x3D82", "send":{"Dimmer":128} }
      +ZbSend { "device":"0x3D82", "send":{"Dimmer":254} }
      +ZbSend { "device":"0x3D82", "endpoint":"0x0B", "send":{"Dimmer":0} }
      +

      Receiving Commands~

      If you pair devices such as switches or remotes, you will also receive commands from those devices.

      When a command is received, attributes are published both in their low-level and high-level formats (if known).

      Low level format is the following: "<cluster>!<cmd>":"<payload"

      IKEA On/Off Switch

      {"ZbReceived":{"0x3476":{"Device":"0x3476","Name":"ikea_switch","0006!01":"","Power":1,"Endpoint":1,"LinkQuality":134}}}

      The command received "0006!01":"" is Power On (0x01) from On/Off cluster (0x0006) with no payload. It is also translated as "Power":1. "Endpoint":1 tells you from which endpoint the command was sent.

      Light State Tracking~

      Once Z2T receives a command related to a light (Power, Dimmer, Color, ColorTemp), it sends right after a Read command to get the actual state of the light. This is used for Hue Emulation and Alexa support. The final attributes are read betwenn 200ms and 1000ms later, to allow for the light to achieve its target state.

      Example

      16:02:04 MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_remote":{"Device":"0xF72F","0006!02":"","Power":2,"Endpoint":1,"Group":100,"LinkQuality":75}}}
      +16:02:05 MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","Power":true,"Endpoint":1,"LinkQuality":80}}}
      +16:02:06 MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_remote":{"Device":"0xF72F","0008!06":"002B0500","DimmerUp":true,"Endpoint":1,"Group":100,"LinkQuality":75}}}
      +16:02:08 MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","Dimmer":102,"Endpoint":1,"LinkQuality":80}}}
      +

      Example~

      Ikea Tradfri Remote received commands:

      • Short press center button - "0006!02":"" and "Power":2
      • Short press dimmer up - "0008!06":"002B0500" and "DimmerUp":true
      • Short press dimmer down - "0008!02":"012B05000000" and "DimmerStep":1
      • Short press arrow right - "0005!07":"00010D00" and "ArrowClick":0
      • Short press arrow left - "0xF72F","0005!07":"01010D00" and "ArrowClick":1
      • Long press dimmer up - "0008!05":"0054" and "DimmerMove":0
      • Long press dimmer up release - "0008!07":"" and "DimmerStop":true
      • Long press dimmer down - "0008!01":"01540000" and "DimmerMove":1
      • Long press dimmer down release - ,"0008!03":"0000" and "DimmerStop":true

      Zigbee Binding~

      Binding allows a device to send command to another device in the same Zigbee network, without any additional logic. For example, you can set a remote to control directly a group of lights, without any rules on the coordinator. The coordinator will still receive all commands.

      Example of direct binding ZbBind {"Device":"0xC2EF","ToDevice":"0x5ADF","Endpoint":1,"ToEndpoint":1,"Cluster":6}

      This command links the device 0xC2EF that will send all commands for cluster 6 (On/off cluster) from endpoint 1 to the target device 0x5ADF on endpoint 1.

      Example of group binding ZbBind {"Device":"0xC2EF","ToGroup":100,"Endpoint":1,"Cluster":6}

      This command links the device 0xC2EF that will send all commands for cluster 6 (On/off cluster) and from endpoint 1 to the group 100.

      Response in case of success

      MQT: tele/%topic%/RESULT = {"ZbBind":{"Device":"0xF72F","Name":"IKEA_Remote","Status":0,"StatusMessage":"SUCCESS"}}
      +

      Example: IKEA remote and IKEA Light~

      IKEA remotes only support 1 group and can be linked to a light only via group numbers (no direct binding).

      1. Add the light to group 100 ZbSend {"device":"IKEA_Light","Send":{"AddGroup":100}}

      2. Bind the remote to group 100. Note: you need to press a button on the remote right before sending this command to make sure it's not in sleep mode ZbBind {"Device":"IKEA_Remote","ToGroup":100,"Endpoint":1,"Cluster":6}

      Zigbee Groups~

      Zigbee has a unique feature called Groups. It allows you to send a single command to a group of devices. For example: a remote can control a group of multiple lights when grouped.

      Zigbee groups are 16 bits arbitrary numbers that you can freely assign. When you send to a group, you don't specify a target address anymore, nor an endpoint.

      Groups works in two steps: first you add devices to groups, second you send commands to groups. See Zigbee Binding on how to configure a remote to send commands to a specific group.

      Configuring groups for devices requires sending commands. Make sure the device is powered and awake (wake-up battery powered devices).

      List all groups for a device~

      ZbSend {"device":"IKEA_Light","Send":{"GetAllGroups":true}}

      MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<02":"FF00","GetGroupCapacity":255,"GetGroupCount":0,"GetGroup":[],"Endpoint":1,"LinkQuality":80}}}
      +

      The following response tells you: "GetGroupCount":1 the light belongs to one group "GetGroup":[100] and the group number is 100.

      MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<02":"FF016400","GetGroupCapacity":255,"GetGroupCount":1,"GetGroup":[100],"Endpoint":1,"LinkQuality":80}}}
      +

      Assign a group to a device~

      ZbSend {"device":"IKEA_Light","Send":{"AddGroup":100}}

      MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<00":"006400","AddGroup":100,"AddGroupStatus":0,"AddGroupStatusMsg":"SUCCESS","Endpoint":1,"LinkQuality":80}}}
      +

      Or if the group already exists:

      MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<00":"8A6400","AddGroup":100,"AddGroupStatus":138,"AddGroupStatusMsg":"DUPLICATE_EXISTS","Endpoint":1,"LinkQuality":80}}}
      +

      Remove a group~

      ZbSend {"device":"IKEA_Light","Send":{"RemoveGroup":100}}

      MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<03":"006400","RemoveGroup":100,"RemoveGroupStatus":0,"RemoveGroupStatusMsg":"SUCCESS","Endpoint":1,"LinkQuality":80}}}
      +

      or if the group does not exist

      MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<03":"8B6400","RemoveGroup":100,"RemoveGroupStatus":139,"RemoveGroupStatusMsg":"NOT_FOUND","Endpoint":1,"LinkQuality":80}}}
      +

      Remove all groups~

      ZbSend {"device":"IKEA_Light","Send":{"RemoveAllGroups":true}}

      MQT: tele/%topic%/SENSOR = {"ZbResponse":{"Device":"0x5ADF","Name":"IKEA_Light","Command":"0004!04","Status":0,"StatusMessage":"SUCCESS","Endpoint":1,"LinkQuality":80}}
      +

      Sending commands to a group~

      Just use the attribute "Group":<group_id> instead of "Device":<device> when sending a command.

      Example:

      • power on all light in group 100: ZbSend {"group":100,"Send":{"Power":1}}
      • set all dimmers in group 100 to 50%: ZbSend {"group":100,"Send":{"Dimmer":127}}

      Zigbee and Hue Emulation for Alexa~

      Z2T now supports Hue Emulation for Zigbee lights. It will mimic most of Zigbee gateways, and allows you to control Zigbee lights directly with Alexa, without any MQTT broker nor Alexa skill.

      Command ZbLight configures a Zigbee device to be Alexa controllable. Specify the number of channels the light supports:

      • 0 Simple On/Off light
      • 1 White Light with Dimmer
      • 2 White Light with Dimmer and Cold/Warm White
      • 3 RGB Light
      • 4 RGBW Light
      • 5 RGBCW Light, RGB and Cold/Warm White

      To set the light, use ZbLight <device>,<nb_of_channels. Ex:

      ZbLight 0x1234,2
      +ZbLight Kitchen_Light,1   (see ZbName)
      +

      Once a light is declared, Z2T will monitor any change made to the light via Z2T or via remotes, either from a direct message or via a group message. Z2T will then send a read command to the light, between 200ms and 1000ms later, and memorize the last value.

      To read the last known status of a light, use ZbLight <device>

      Example

      ZbLight Kitchen_Light

      MQT: stat/%topic%/RESULT = {"ZbLight":{"Kitchen_Light":{"Device":"0x5ADF","Light":2,"Power":0,"Dimmer":130,"Colormode":2,"CT":350}}}
      +

      Specific Device Configuration~

      If your device pairs successfully with Zigbee2Tasmota but doesn't report on standardised endpoints you will see messages similar to: {"ZbReceived":{"0x099F":{"0500!00":"010000FF0000","LinkQuality":70}}} {"ZbReceived":{"0x7596":{"0006!01":"","LinkQuality":65}}}

      In this case you will have to use rules or an external home automation solution to parse those messages. The following section will focus only on rules to utilize the device inside Tasmota ecosystem.

      Aqara Water Leak Sensor~

      "ModelId":"lumi.sensor_wleak.aq1"

      In this example sensor reports on 0x099F and sends an mqtt message to topic stat/leak_sensor/LEAK:

      Rule
      +  on ZbReceived#0x099F#0500!00=010000FF0000 do publish stat/leak_sensor/LEAK ON endon
      +  on ZbReceived#0x099F#0500!00=000000FF0000 do publish stat/leak_sensor/LEAK OFF endon
      +

      Aqara Vibration Sensor~

      "ModelId":"lumi.vibration.aq1"

      To modify sensor sensitivity use command. Replace "device" with your own device name:

      # for high sensitivity
      +ZbSend {"device":"vibration","Endpoint":1,"Cluster":0,"Manuf":"0x115F","Write":{"0000/FF0D%20":"0x01"}}
      +# for medium sensitivity
      +ZbSend {"device":"vibration","Endpoint":1,"Cluster":0,"Manuf":"0x115F","Write":{"0000/FF0D%20":"0x0B"}}
      +# for low sensitivity
      +ZbSend {"device":"vibration","Endpoint":1,"Cluster":0,"Manuf":"0x115F","Write":{"0000/FF0D%20":"0x15"}}
      +
      Command needs to be issued shortly after pressing the device button. There will be no response to the command but you can check if the new option is active by using

      ZbSend {"Device":"vibration","Endpoint":1,"Cluster":0,"Manuf":"0x115F","Read":"0xFF0D"}
      +
      Received response will be :
      {"ZbReceived":{"vibration":{"Device":"0x0B2D","Name":"vibration","0000/FF0D":1,"Endpoint":1,"LinkQuality":72}}}
      +
      "0000/FF0D" is the key, value 1 is high sensitivity, 11 medium and 21 is low.

      Osram/Ledvance Smart+ Switch Mini~

      "ModelId":"Lightify Switch Mini"

      To pair the Smart+ Switch Mini with the Bridge you need to hold Arrow Up and Middle Button for 3 seconds. Each Button is linked to another Endpoint. Arrow Upis Endpoint 1, Arrow Down is Endpoint 2 and Middle Buttonis Endpoint 3. To link the Smart+ Switch Mini with IKEA Tradfri dimmable lights, I used the following commands

      The IKEA light needs to be Group 100for this example.

      # for Power On and Power Off
      +ZbBind {"Device":"Name","ToGroup":100,"Endpoint":1,"Cluster":6}
      +ZbBind {"Device":"Name","ToGroup":100,"Endpoint":2,"Cluster":6}
      +# for dimming
      +ZbBind {"Device":"Name","ToGroup":100,"Endpoint":1,"Cluster":8}
      +ZbBind {"Device":"Name","ToGroup":100,"Endpoint":2,"Cluster":8}
      +

      Sandy Beach/Tuya Thermostatic Radiator Valve (TS0601)~

      To pair the TRV to the Bridge you need to press and hold the Auto/Manual (8 o'clock position) and the Comfort/Energy Saving (4 o'clock position) buttons at the same time until the signal indicator flashes. The Bridge will then automatically find the following useful parameters

      Ambient temperature (C)     (LocalTemperature)
      +
      +Temperature set point (C)   (TuyaTempTarget)
      +
      +Valve position (0-100%)     (TuyaValvePosition)
      +

      To set the Temperature Set Point use

      ZbSend {"Device": "0xXXXX", "Write":{"TuyaTempTarget":XX}}
      +
      The 'Device' can be the DeviceID 0x0000 or the ZbName so if a device has been named
      ZbName 0x1234,Utility
      +ZbSend {"Device": "0x1234", "Write":{"TuyaTempTarget":20}}
      +
      and
      ZbSend {"Device": "Utility", "Write":{"TuyaTempTarget":20}}
      +
      will set the TRV setpoint to 20C. Other commands will be available and will be added when clarified.

      Advanced Topic: Zigbee Device Plugin~

      Zigbee2Tasmota supports most common and standard attributes from ZCL, see here.

      But creativity of manufacturers is limitless. Some manufacturers make mistakes compared to the Zigbee ZCL standard (maybe because their developers didn't read the specifications thoroughly), and others invent their own standards - ex: TuyA devices sometimes use a encapsulation of TuYa Serial protocol in Zigbee.

      The Zigbee plug-in mechanisms is composed of simple text files to describe device-specific non-standard attributes or non-standard mapping of values.

      You simply need to copy the required file(s) in the coordinator's filesystem and restart. You should see logs similar to the following after Zigbee has started.

      ZIG: Zigbee started
      +ZIG: Zigbee device information found in File System (1 devices - 48 bytes)
      +ZIG: Zigbee device data in File System (20 bytes)
      +ZIG: ZbLoad '<internal_plugin>' loaded successfully
      +ZIG: ZbLoad 'TS0001_switch.zb' loaded successfully
      +

      Writing Zigbee plugins~

      A Zigbee plugin file needs to have .zb extension and start with the first line:

      #Z2Tv1
      +

      The plugin file has 3 types of declarations:

      • a device match pattern, specifies which model and/or manufacturer identifiers to match
      • an attribute definition, defines a new attribute name
      • an attribute synonym, remaps the incoming attribute to a new attribute (or the same) and applies multiplier/divisor
      • # is a marker for comments, and everything from # to end of line is ignored

      Note: Zigbee plugins currently only handles Zigbee attributes (read, write, report) but not Zigbee commands which can't be remapped. There hasn't been any need for command remapping but who knows...

      Matching a device or a family of devices~

      A plugin section needs to start with one or more matching patterns. All the following statements share the same matching pattern until a new pattern appears.

      A pattern is of form: :<modelid>,<manufecturerid>. Possible values are:

      • "match all", if empty the pattern matches all devices
      • "exact match", if a value is provided, the model or manufacturer value must exactly match
      • "starts with", if a value ends with *, any value starting with this value macthes

      Example:

      Pattern Description
      :, Matches all devices
      :TRADFRI*,
      :SYMFONISK*,
      Matches any device with ModelID starting with TRADFRI or SYMFONISK
      :TS0201,_TZ3000_ywagc4rj Match only if DeviceID is TS0201 and ManufacturerID is _TZ3000_ywagc4rj

      Only the first succesful match is applied, all subsequent statements are ignored. You may need to adjust the order of files if priority is needed between plugins

      Defining a new attribute~

      You can define or overwrite an attribute name for a specific cluser/attributeid, and apply optional multiplier/divider.

      The format is:

      <cluster>/<attributeid>[,%<type>],<name>[,mul:<mul>][,div:<div>][,add:<add>][,manuf:<manuf>]

      Parameter Description
      <cluster> Cluster number in 4 digits hex
      Example: 0006 for cluster 6
      <attributeid> Attribute identifier in 4 digits hex
      Example: 0001 for attribute id 1
      %<type> (optional) Type of the attribute either in 2 digits hex format or using the Zigbee type name
      Example: %21 or %uint16
      mul:<mul> (optional) 1..65535: Apply a multiplier to the value received
      div:<div> (optional) 1..65535: Apply a divider to the value received (after the multiplier is applied)
      add:<add> (optional) -36278..32767: Add/substract a value (after multiplier and divider are applied)
      manuf:<manuf> (optional) Add a manufacturer specific code in 4 digits hex

      When a value is received, the following formula is applied (computed using integers):

      new_val = add + (val * mul) / div

      The inverse formula is applied when writing back an attribute. Beware of rounding errors.

      Special case of Tuya attributes (cluster 0xEF00)

      For attributes from the Tuya cluster 0xEF00 the attribute has the form AABB where AA is the type and BB is the dpid. If you only care about receiving attributes, you can use FF as a type so Tasmota accepts any value. To be able to write the attribute, the type must be specified.

      Example: EF00/FF02 accepts any value for dpip 2, while EF00/0202 specifies the type 02 for this dpid. The regular Zigbee type %<type> is unused with Tuya attributes.

      Tuya type Description
      00 raw bytes decoded as hex (n bytes)
      01 bool (1 byte)
      02 int32 (4 bytes)
      03 string (n bytes)
      04 enum (1 byte)
      05 bitmap (1/2/4 bytes)

      Defining an attribute synonym~

      An attribute synonym can remap an incoming attribute value to another attribute and apply the regular transformation. It can also be used to fix a value and keep the same attribute.

      The format is:

      <cluster>/<attributeid>=<new_cluster>/<new_attributedid>[,mul:<mul>][,div:<div>][,add:<add>]

      Parameter Description
      <cluster> Cluster number in 4 digits hex
      <attributeid> Attribute identifier in 4 digits hex
      <new_cluster> Cluster number in 4 digits hex
      <new_attributeid> Attribute identifier in 4 digits hex
      mul:<mul> (optional) 1..65535: Apply a multiplier to the value received
      div:<div> (optional) 1..65535: Apply a divider to the value received (after the multiplier is applied)
      add:<add> (optional) -36278..32767: Add/substract a value (after multiplier and divider are applied)

      Currently the inverse attribute mapping is not done when writing an attribute.

      Troubleshooting~

      While all *.zb files are automatically loaded at startup, you can manually unload a file with ZbUnload <file.zb> and load a modified version with ZbLoad <file.zb>.

      You can dump all the plugins loaded in memory with ZbLoadDump.

      When a synonym is applied, you can see it in logs with loglevel 3 or more:

      ZIG: apply synonym 000C/0055 with 0B04/050B (mul:1 div:1)

      Complete examples~

      Default plugin~

      Below is the default plug-in stored in Flash <internal_plugin> and automatically loaded. It handles the following:

      • solve a bug in IKEA device where the BatteryPercentage is not multiplied by 2
      • map the Power attribute of Aqara magnet window sensor to a synthetic attribute 0500/FFF2 for specific handling
      #Z2Tv1
      +:TRADFRI*,
      +:SYMFONISK*,
      +0001/0021=0001/0021,mul:2
      +:lumi.sensor_magnet*,
      +0006/0000=0500/FFF2
      +

      Tuya Moes thermostat humidity bug~

      Tuya_KCTW1Z.zb fixes a bug where humidity should be multiplied by 10.

      #Z2Tv1
      +# Tuya fix humidity by 10 
      +# https://zigbee.blakadder.com/Tuya_KCTW1Z.html
      +:TS0201,_TZ3000_ywagc4rj
      +0405/0000=0405/0000,mul:10
      +

      GiEX garden watering~

      The following plugin defines device specific Tuya attributes, and matches the BatteryPercentage to the regular ZCL attribute (multiplied by 2).

      #Z2Tv1
      +# GiEX garden watering https://www.aliexpress.com/item/1005004222098040.html
      +:TS0601,_TZE200_sh1btabb
      +EF00/0101,WaterMode                 # duration=0 / capacity=1
      +EF00/0102,WaterState                # off=0 / on=1
      +EF00/0365,IrrigationStartTime       # (string) ex: "08:12:26"
      +EF00/0366,IrrigationStopTime        # (string) ex: "08:13:36"
      +EF00/0267,CycleIrrigationNumTimes   # number of cycle irrigation times, set to 0 for single cycle
      +EF00/0268,IrrigationTarget          # duration in minutes or capacity in Liters (depending on mode)
      +EF00/0269,CycleIrrigationInterval   # cycle irrigation interval (minutes, max 1440)
      +EF00/026A,CurrentTemperature        # (value ignored because isn't a valid tempurature reading.  Misdocumented and usage unclear)
      +EF00/026C=0001/0021,mul:2           # match to BatteryPercentage
      +EF00/026F,WaterConsumed             # water consumed (Litres)
      +EF00/0372,LastIrrigationDuration    # (string) Ex: "00:01:10,0"
      +

      Advanced topic: Zigbee Reference~

      Tasmota includes plain text aliases for most of the common ZCL attributes (click to expand)
      Alias Cluster Attribute Type
      ZCLVersion 0x0000 0x0000 %20 - uint8
      AppVersion 0x0000 0x0001 %20 - uint8
      StackVersion 0x0000 0x0002 %20 - uint8
      HWVersion 0x0000 0x0003 %20 - uint8
      Manufacturer 0x0000 0x0004 %42 - string
      ModelId 0x0000 0x0005 %42 - string
      DateCode 0x0000 0x0006 %42 - string
      PowerSource 0x0000 0x0007 %30 - enum8
      GenericDeviceClass 0x0000 0x0008 %30 - enum8
      GenericDeviceType 0x0000 0x0009 %30 - enum8
      ProductCode 0x0000 0x000A %41 - octstr
      ProductURL 0x0000 0x000B %42 - string
      LocationDescription 0x0000 0x0010 %42 - string
      PhysicalEnvironment 0x0000 0x0011 %30 - enum8
      DeviceEnabled 0x0000 0x0012 %10 - bool
      AlarmMask 0x0000 0x0013 %18 - map8
      DisableLocalConfig 0x0000 0x0014 %18 - map8
      SWBuildID 0x0000 0x4000 %42 - string
      MullerLightMode 0x0000 0x4005 %20 - uint8
      MainsVoltage 0x0001 0x0000 %21 - uint16
      MainsFrequency 0x0001 0x0001 %20 - uint8
      MainsAlarmMask 0x0001 0x0010 %18 - map8
      MainsVoltageMinThreshold 0x0001 0x0011 %21 - uint16
      MainsVoltageMaxThreshold 0x0001 0x0012 %21 - uint16
      MainsVoltageDwellTripPoint 0x0001 0x0013 %21 - uint16
      BatteryVoltage 0x0001 0x0020 %20 - uint8
      BatteryPercentage 0x0001 0x0021 %20 - uint8
      BatteryManufacturer 0x0001 0x0030 %42 - string
      BatterySize 0x0001 0x0031 %30 - enum8
      BatteryAHrRating 0x0001 0x0032 %21 - uint16
      BatteryQuantity 0x0001 0x0033 %20 - uint8
      BatteryRatedVoltage 0x0001 0x0034 %20 - uint8
      BatteryAlarmMask 0x0001 0x0035 %18 - map8
      BatteryVoltageMinThreshold 0x0001 0x0036 %20 - uint8
      BatteryVoltageThreshold1 0x0001 0x0037 %20 - uint8
      BatteryVoltageThreshold2 0x0001 0x0038 %20 - uint8
      BatteryVoltageThreshold3 0x0001 0x0039 %20 - uint8
      BatteryPercentageMinThreshold 0x0001 0x003A %20 - uint8
      BatteryPercentageThreshold1 0x0001 0x003B %20 - uint8
      BatteryPercentageThreshold2 0x0001 0x003C %20 - uint8
      BatteryPercentageThreshold3 0x0001 0x003D %20 - uint8
      BatteryAlarmState 0x0001 0x003E %1B - map32
      CurrentTemperature 0x0002 0x0000 %29 - int16
      MinTempExperienced 0x0002 0x0001 %29 - int16
      MaxTempExperienced 0x0002 0x0002 %29 - int16
      OverTempTotalDwell 0x0002 0x0003 %21 - uint16
      DeviceTempAlarmMask 0x0002 0x0010 %18 - map8
      LowTempThreshold 0x0002 0x0011 %29 - int16
      HighTempThreshold 0x0002 0x0012 %29 - int16
      LowTempDwellTripPoint 0x0002 0x0013 %22 - uint24
      HighTempDwellTripPoint 0x0002 0x0014 %22 - uint24
      IdentifyTime 0x0003 0x0000 %21 - uint16
      GroupNameSupport 0x0004 0x0000 %18 - map8
      SceneCount 0x0005 0x0000 %20 - uint8
      CurrentScene 0x0005 0x0001 %20 - uint8
      CurrentGroup 0x0005 0x0002 %21 - uint16
      SceneValid 0x0005 0x0003 %10 - bool
      SceneNameSupport 0x0005 0x0004 %18 - map8
      LastConfiguredBy 0x0005 0x0005 %F0 - EUI64
      Power 0x0006 0x0000 %10 - bool
      StartUpOnOff 0x0006 0x4003 %30 - enum8
      Power 0x0006 0x8000 %10 - bool
      OnOff 0x0006 0x4000 %10 - bool
      OnTime 0x0006 0x4001 %21 - uint16
      OffWaitTime 0x0006 0x4002 %21 - uint16
      SwitchType 0x0007 0x0000 %30 - enum8
      SwitchActions 0x0007 0x0010 %30 - enum8
      Dimmer 0x0008 0x0000 %20 - uint8
      DimmerRemainingTime 0x0008 0x0001 %21 - uint16
      DimmerMinLevel 0x0008 0x0002 %20 - uint8
      DimmerMaxLevel 0x0008 0x0003 %20 - uint8
      DimmerCurrentFrequency 0x0008 0x0004 %21 - uint16
      DimmerMinFrequency 0x0008 0x0005 %21 - uint16
      DimmerMaxFrequency 0x0008 0x0006 %21 - uint16
      OnOffTransitionTime 0x0008 0x0010 %21 - uint16
      OnLevel 0x0008 0x0011 %20 - uint8
      OnTransitionTime 0x0008 0x0012 %21 - uint16
      OffTransitionTime 0x0008 0x0013 %21 - uint16
      DefaultMoveRate 0x0008 0x0014 %21 - uint16
      DimmerOptions 0x0008 0x000F %18 - map8
      DimmerStartUpLevel 0x0008 0x4000 %20 - uint8
      AlarmCount 0x0009 0x0000 %21 - uint16
      Time 0x000A 0x0000 %E2 - UTC
      TimeStatus 0x000A 0x0001 %18 - map8
      TimeZone 0x000A 0x0002 %2B - int32
      DstStart 0x000A 0x0003 %23 - uint32
      DstEnd 0x000A 0x0004 %23 - uint32
      DstShift 0x000A 0x0005 %2B - int32
      StandardTime 0x000A 0x0006 %23 - uint32
      LocalTime 0x000A 0x0007 %23 - uint32
      LastSetTime 0x000A 0x0008 %E2 - UTC
      ValidUntilTime 0x000A 0x0009 %E2 - UTC
      TimeEpoch 0x000A 0xFF00 %E2 - UTC
      LocationType 0x000B 0x0000 %08 - data8
      LocationMethod 0x000B 0x0001 %30 - enum8
      LocationAge 0x000B 0x0002 %21 - uint16
      QualityMeasure 0x000B 0x0003 %20 - uint8
      NumberOfDevices 0x000B 0x0004 %20 - uint8
      Coordinate1 0x000B 0x0010 %29 - int16
      Coordinate2 0x000B 0x0011 %29 - int16
      Coordinate3 0x000B 0x0012 %29 - int16
      LocationPower 0x000B 0x0013 %29 - int16
      PathLossExponent 0x000B 0x0014 %21 - uint16
      ReportingPeriod 0x000B 0x0015 %21 - uint16
      CalculationPeriod 0x000B 0x0016 %21 - uint16
      NumberRSSIMeasurements 0x000B 0x0016 %20 - uint8
      AnalogInDescription 0x000C 0x001C %42 - string
      AnalogInMaxValue 0x000C 0x0041 %39 - single
      AnalogInMinValue 0x000C 0x0045 %39 - single
      AnalogInOutOfService 0x000C 0x0051 %10 - bool
      AnalogValue 0x000C 0x0055 %39 - single
      AnalogInReliability 0x000C 0x0067 %30 - enum8
      AnalogInResolution 0x000C 0x006A %39 - single
      AnalogInStatusFlags 0x000C 0x006F %18 - map8
      AnalogInEngineeringUnits 0x000C 0x0075 %31 - enum16
      AnalogInApplicationType 0x000C 0x0100 %23 - uint32
      AqaraRotate 0x000C 0xFF55 %21 - uint16
      Aqara_FF05 0x000C 0xFF05 %21 - uint16
      AnalogOutDescription 0x000D 0x001C %42 - string
      AnalogOutMaxValue 0x000D 0x0041 %39 - single
      AnalogOutMinValue 0x000D 0x0045 %39 - single
      AnalogOutOutOfService 0x000D 0x0051 %10 - bool
      AnalogOutValue 0x000D 0x0055 %39 - single
      AnalogOutReliability 0x000D 0x0067 %30 - enum8
      AnalogOutRelinquishDefault 0x000D 0x0068 %39 - single
      AnalogOutResolution 0x000D 0x006A %39 - single
      AnalogOutStatusFlags 0x000D 0x006F %18 - map8
      AnalogOutEngineeringUnits 0x000D 0x0075 %31 - enum16
      AnalogOutApplicationType 0x000D 0x0100 %23 - uint32
      AnalogDescription 0x000E 0x001C %42 - string
      AnalogOutOfService 0x000E 0x0051 %10 - bool
      AnalogValue 0x000E 0x0055 %39 - single
      AnalogPriorityArray 0x000E 0x0057 %FF - unk
      AnalogReliability 0x000E 0x0067 %30 - enum8
      AnalogRelinquishDefault 0x000E 0x0068 %39 - single
      AnalogStatusFlags 0x000E 0x006F %18 - map8
      AnalogEngineeringUnits 0x000E 0x0075 %31 - enum16
      AnalogApplicationType 0x000E 0x0100 %23 - uint32
      BinaryInActiveText 0x000F 0x0004 %42 - string
      BinaryInDescription 0x000F 0x001C %42 - string
      BinaryInInactiveText 0x000F 0x002E %42 - string
      BinaryInOutOfService 0x000F 0x0051 %10 - bool
      BinaryInPolarity 0x000F 0x0054 %30 - enum8
      BinaryInValue 0x000F 0x0055 %10 - bool
      BinaryInReliability 0x000F 0x0067 %30 - enum8
      BinaryInStatusFlags 0x000F 0x006F %18 - map8
      BinaryInApplicationType 0x000F 0x0100 %23 - uint32
      BinaryOutActiveText 0x0010 0x0004 %42 - string
      BinaryOutDescription 0x0010 0x001C %42 - string
      BinaryOutInactiveText 0x0010 0x002E %42 - string
      BinaryOutMinimumOffTime 0x0010 0x0042 %23 - uint32
      BinaryOutMinimumOnTime 0x0010 0x0043 %23 - uint32
      BinaryOutOutOfService 0x0010 0x0051 %10 - bool
      BinaryOutPolarity 0x0010 0x0054 %30 - enum8
      BinaryOutValue 0x0010 0x0055 %10 - bool
      BinaryOutReliability 0x0010 0x0067 %30 - enum8
      BinaryOutRelinquishDefault 0x0010 0x0068 %10 - bool
      BinaryOutStatusFlags 0x0010 0x006F %18 - map8
      BinaryOutApplicationType 0x0010 0x0100 %23 - uint32
      BinaryActiveText 0x0011 0x0004 %42 - string
      BinaryDescription 0x0011 0x001C %42 - string
      BinaryInactiveText 0x0011 0x002E %42 - string
      BinaryMinimumOffTime 0x0011 0x0042 %23 - uint32
      BinaryMinimumOnTime 0x0011 0x0043 %23 - uint32
      BinaryOutOfService 0x0011 0x0051 %10 - bool
      BinaryValue 0x0011 0x0055 %10 - bool
      BinaryReliability 0x0011 0x0067 %30 - enum8
      BinaryRelinquishDefault 0x0011 0x0068 %10 - bool
      BinaryStatusFlags 0x0011 0x006F %18 - map8
      BinaryApplicationType 0x0011 0x0100 %23 - uint32
      MultiInDescription 0x0012 0x001C %42 - string
      MultiInNumberOfStates 0x0012 0x004A %21 - uint16
      MultiInOutOfService 0x0012 0x0051 %10 - bool
      MultiInValue 0x0012 0x0055 %21 - uint16
      MultiInReliability 0x0012 0x0067 %30 - enum8
      MultiInStatusFlags 0x0012 0x006F %18 - map8
      MultiInApplicationType 0x0012 0x0100 %23 - uint32
      MultiOutDescription 0x0013 0x001C %42 - string
      MultiOutNumberOfStates 0x0013 0x004A %21 - uint16
      MultiOutOutOfService 0x0013 0x0051 %10 - bool
      MultiOutValue 0x0013 0x0055 %21 - uint16
      MultiOutReliability 0x0013 0x0067 %30 - enum8
      MultiOutRelinquishDefault 0x0013 0x0068 %21 - uint16
      MultiOutStatusFlags 0x0013 0x006F %18 - map8
      MultiOutApplicationType 0x0013 0x0100 %23 - uint32
      MultiDescription 0x0014 0x001C %42 - string
      MultiNumberOfStates 0x0014 0x004A %21 - uint16
      MultiOutOfService 0x0014 0x0051 %10 - bool
      MultiValue 0x0014 0x0055 %21 - uint16
      MultiReliability 0x0014 0x0067 %30 - enum8
      MultiRelinquishDefault 0x0014 0x0068 %21 - uint16
      MultiStatusFlags 0x0014 0x006F %18 - map8
      MultiApplicationType 0x0014 0x0100 %23 - uint32
      TotalProfileNum 0x001A 0x0000 %20 - uint8
      MultipleScheduling 0x001A 0x0001 %10 - bool
      EnergyFormatting 0x001A 0x0002 %18 - map8
      EnergyRemote 0x001A 0x0003 %10 - bool
      ScheduleMode 0x001A 0x0004 %18 - map8
      CheckinInterval 0x0020 0x0000 %23 - uint32
      LongPollInterval 0x0020 0x0001 %23 - uint32
      ShortPollInterval 0x0020 0x0002 %21 - uint16
      FastPollTimeout 0x0020 0x0003 %21 - uint16
      CheckinIntervalMin 0x0020 0x0004 %23 - uint32
      LongPollIntervalMin 0x0020 0x0005 %23 - uint32
      FastPollTimeoutMax 0x0020 0x0006 %21 - uint16
      MaxSinkTableEntries 0x0021 0x0000 %20 - uint8
      SinkTable 0x0021 0x0001 %43 - octstr16
      CommunicationMode 0x0021 0x0002 %18 - map8
      CcommissioningExitMode 0x0021 0x0003 %18 - map8
      CommissioningWindow 0x0021 0x0004 %21 - uint16
      SecurityLevel 0x0021 0x0005 %18 - map8
      ServerFunctionality 0x0021 0x0006 %1A - map24
      ServerActiveFunctionality 0x0021 0x0007 %1A - map24
      MaxProxyTableEntries 0x0021 0x0010 %20 - uint8
      ProxyTable 0x0021 0x0011 %43 - octstr16
      NotificationRetryNumber 0x0021 0x0012 %20 - uint8
      NotificationRetryTimer 0x0021 0x0013 %20 - uint8
      MaxSearchCounter 0x0021 0x0014 %20 - uint8
      BlockedGPDID 0x0021 0x0015 %43 - octstr16
      ClientFunctionality 0x0021 0x0016 %1A - map24
      ClientActiveFunctionality 0x0021 0x0017 %1A - map24
      SharedSecurityKeyType 0x0021 0x0020 %18 - map8
      SharedSecurityKey 0x0021 0x0021 %F1 - key128
      LinkKey 0x0021 0x0022 %F1 - key128
      PhysicalClosedLimit 0x0100 0x0000 %21 - uint16
      MotorStepSize 0x0100 0x0001 %20 - uint8
      Status 0x0100 0x0002 %18 - map8
      ClosedLimit 0x0100 0x0010 %21 - uint16
      Mode 0x0100 0x0011 %30 - enum8
      LockState 0x0101 0x0000 %30 - enum8
      LockType 0x0101 0x0001 %30 - enum8
      ActuatorEnabled 0x0101 0x0002 %10 - bool
      DoorState 0x0101 0x0003 %30 - enum8
      DoorOpenEvents 0x0101 0x0004 %23 - uint32
      DoorClosedEvents 0x0101 0x0005 %23 - uint32
      OpenPeriod 0x0101 0x0006 %21 - uint16
      NumberOfLogRecordsSupported 0x0101 0x0010 %21 - uint16
      NumberOfTotalUsersSupported 0x0101 0x0011 %21 - uint16
      NumberOfPINUsersSupported 0x0101 0x0012 %21 - uint16
      NumberOfRFIDUsersSupported 0x0101 0x0013 %21 - uint16
      NumberOfWeekDaySchedulesSupportedPerUser 0x0101 0x0014 %20 - uint8
      NumberOfYearDaySchedulesSupportedPerUser 0x0101 0x0015 %20 - uint8
      NumberOfHolidaySchedulesSupported 0x0101 0x0016 %20 - uint8
      MaxPINCodeLength 0x0101 0x0017 %20 - uint8
      MinPINCodeLength 0x0101 0x0018 %20 - uint8
      MaxRFIDCodeLength 0x0101 0x0019 %20 - uint8
      MinRFIDCodeLength 0x0101 0x0011 %20 - uint8
      LockEnableLogging 0x0101 0x0020 %10 - bool
      LockLanguage 0x0101 0x0021 %42 - string
      LockLEDSettings 0x0101 0x0022 %20 - uint8
      AutoRelockTime 0x0101 0x0023 %23 - uint32
      LockSoundVolume 0x0101 0x0024 %20 - uint8
      LockOperatingMode 0x0101 0x0025 %30 - enum8
      LockSupportedOperatingModes 0x0101 0x0026 %19 - map16
      LockDefaultConfigurationRegister 0x0101 0x0027 %19 - map16
      LockEnableLocalProgramming 0x0101 0x0028 %10 - bool
      LockEnableOneTouchLocking 0x0101 0x0029 %10 - bool
      LockEnableInsideStatusLED 0x0101 0x002A %10 - bool
      LockEnablePrivacyModeButton 0x0101 0x002B %10 - bool
      LockAlarmMask 0x0101 0x0040 %19 - map16
      LockKeypadOperationEventMask 0x0101 0x0041 %19 - map16
      LockRFOperationEventMask 0x0101 0x0042 %19 - map16
      LockManualOperationEventMask 0x0101 0x0043 %19 - map16
      LockRFIDOperationEventMask 0x0101 0x0044 %19 - map16
      LockKeypadProgrammingEventMask 0x0101 0x0045 %19 - map16
      LockRFProgrammingEventMask 0x0101 0x0046 %19 - map16
      LockRFIDProgrammingEventMask 0x0101 0x0047 %19 - map16
      AqaraVibrationMode 0x0101 0x0055 %21 - uint16
      AqaraVibrationsOrAngle 0x0101 0x0503 %21 - uint16
      AqaraVibration505 0x0101 0x0505 %23 - uint32
      AqaraAccelerometer 0x0101 0x0508 %25 - uint48
      WindowCoveringType 0x0102 0x0000 %30 - enum8
      PhysicalClosedLimitLift 0x0102 0x0001 %21 - uint16
      PhysicalClosedLimitTilt 0x0102 0x0002 %21 - uint16
      CurrentPositionLift 0x0102 0x0003 %21 - uint16
      CurrentPositionTilt 0x0102 0x0004 %21 - uint16
      NumberofActuationsLift 0x0102 0x0005 %21 - uint16
      NumberofActuationsTilt 0x0102 0x0006 %21 - uint16
      ConfigStatus 0x0102 0x0007 %18 - map8
      CurrentPositionLiftPercentage 0x0102 0x0008 %20 - uint8
      CurrentPositionTiltPercentage 0x0102 0x0009 %20 - uint8
      InstalledOpenLimitLift 0x0102 0x0010 %21 - uint16
      InstalledClosedLimitLift 0x0102 0x0011 %21 - uint16
      InstalledOpenLimitTilt 0x0102 0x0012 %21 - uint16
      InstalledClosedLimitTilt 0x0102 0x0013 %21 - uint16
      VelocityLift 0x0102 0x0014 %21 - uint16
      AccelerationTimeLift 0x0102 0x0015 %21 - uint16
      DecelerationTimeLift 0x0102 0x0016 %21 - uint16
      Mode 0x0102 0x0017 %18 - map8
      IntermediateSetpointsLift 0x0102 0x0018 %41 - octstr
      IntermediateSetpointsTilt 0x0102 0x0019 %41 - octstr
      TuyaMovingState 0x0102 0xF000 %30 - enum8
      TuyaCalibration 0x0102 0xF001 %30 - enum8
      TuyaMotorReversal 0x0102 0xF002 %30 - enum8
      TuyaCalibrationTime 0x0102 0xF003 %21 - uint16
      LocalTemperature 0x0201 0x0000 %29 - int16
      OutdoorTemperature 0x0201 0x0001 %29 - int16
      ThermostatOccupancy 0x0201 0x0002 %18 - map8
      AbsMinHeatSetpointLimit 0x0201 0x0003 %29 - int16
      AbsMaxHeatSetpointLimit 0x0201 0x0004 %29 - int16
      AbsMinCoolSetpointLimit 0x0201 0x0005 %29 - int16
      AbsMaxCoolSetpointLimit 0x0201 0x0006 %29 - int16
      PICoolingDemand 0x0201 0x0007 %20 - uint8
      PIHeatingDemand 0x0201 0x0008 %20 - uint8
      HVACSystemTypeConfiguration 0x0201 0x0009 %18 - map8
      LocalTemperatureCalibration 0x0201 0x0010 %28 - int8
      OccupiedCoolingSetpoint 0x0201 0x0011 %29 - int16
      OccupiedHeatingSetpoint 0x0201 0x0012 %29 - int16
      UnoccupiedCoolingSetpoint 0x0201 0x0013 %29 - int16
      UnoccupiedHeatingSetpoint 0x0201 0x0014 %29 - int16
      MinHeatSetpointLimit 0x0201 0x0015 %29 - int16
      MaxHeatSetpointLimit 0x0201 0x0016 %29 - int16
      MinCoolSetpointLimit 0x0201 0x0017 %29 - int16
      MaxCoolSetpointLimit 0x0201 0x0018 %29 - int16
      MinSetpointDeadBand 0x0201 0x0019 %28 - int8
      ThermostatAlarmMask 0x0201 0x001D %18 - map8
      ThermostatRunningMode 0x0201 0x001E %30 - enum8
      RemoteSensing 0x0201 0x001A %18 - map8
      ControlSequenceOfOperation 0x0201 0x001B %30 - enum8
      SystemMode 0x0201 0x001C %30 - enum8
      TRVMode 0x0201 0x4000 %30 - enum8
      ValvePosition 0x0201 0x4001 %20 - uint8
      EurotronicErrors 0x0201 0x4002 %20 - uint8
      CurrentTemperatureSetPoint 0x0201 0x4003 %29 - int16
      EurotronicHostFlags 0x0201 0x4008 %22 - uint24
      TRVMirrorDisplay 0x0201 0xF002 %10 - bool
      TRVBoost 0x0201 0xF004 %10 - bool
      TRVWindowOpen 0x0201 0xF010 %10 - bool
      TRVChildProtection 0x0201 0xF080 %10 - bool
      ThSetpoint 0x0201 0xFFF0 %20 - uint8
      TempTarget 0x0201 0xFFF1 %29 - int16
      FanMode 0x0202 0x0000 %30 - enum8
      FanModeSequence 0x0202 0x0001 %30 - enum8
      RelativeHumidity 0x0203 0x0000 %20 - uint8
      DehumidificationCooling 0x0203 0x0001 %20 - uint8
      RHDehumidificationSetpoint 0x0203 0x0010 %20 - uint8
      RelativeHumidityMode 0x0203 0x0011 %30 - enum8
      DehumidificationLockout 0x0203 0x0012 %30 - enum8
      DehumidificationHysteresis 0x0203 0x0013 %20 - uint8
      DehumidificationMaxCool 0x0203 0x0014 %20 - uint8
      RelativeHumidityDisplay 0x0203 0x0015 %30 - enum8
      TemperatureDisplayMode 0x0204 0x0000 %30 - enum8
      ThermostatKeypadLockout 0x0204 0x0001 %30 - enum8
      ThermostatScheduleProgrammingVisibility 0x0204 0x0002 %30 - enum8
      Hue 0x0300 0x0000 %20 - uint8
      Sat 0x0300 0x0001 %20 - uint8
      RemainingTime 0x0300 0x0002 %21 - uint16
      X 0x0300 0x0003 %21 - uint16
      Y 0x0300 0x0004 %21 - uint16
      DriftCompensation 0x0300 0x0005 %30 - enum8
      CompensationText 0x0300 0x0006 %42 - string
      CT 0x0300 0x0007 %21 - uint16
      ColorMode 0x0300 0x0008 %30 - enum8
      NumberOfPrimaries 0x0300 0x0010 %20 - uint8
      Primary1X 0x0300 0x0011 %21 - uint16
      Primary1Y 0x0300 0x0012 %21 - uint16
      Primary1Intensity 0x0300 0x0013 %20 - uint8
      Primary2X 0x0300 0x0015 %21 - uint16
      Primary2Y 0x0300 0x0016 %21 - uint16
      Primary2Intensity 0x0300 0x0017 %20 - uint8
      Primary3X 0x0300 0x0019 %21 - uint16
      Primary3Y 0x0300 0x001A %21 - uint16
      Primary3Intensity 0x0300 0x001B %20 - uint8
      Primary4X 0x0300 0x0020 %21 - uint16
      Primary4Y 0x0300 0x0021 %21 - uint16
      Primary4Intensity 0x0300 0x0022 %20 - uint8
      Primary5X 0x0300 0x0024 %21 - uint16
      Primary5Y 0x0300 0x0025 %21 - uint16
      Primary5Intensity 0x0300 0x0026 %20 - uint8
      Primary6X 0x0300 0x0028 %21 - uint16
      Primary6Y 0x0300 0x0029 %21 - uint16
      Primary6Intensity 0x0300 0x002A %20 - uint8
      WhitePointX 0x0300 0x0030 %21 - uint16
      WhitePointY 0x0300 0x0031 %21 - uint16
      ColorPointRX 0x0300 0x0032 %21 - uint16
      ColorPointRY 0x0300 0x0033 %21 - uint16
      ColorPointRIntensity 0x0300 0x0034 %20 - uint8
      ColorPointGX 0x0300 0x0036 %21 - uint16
      ColorPointGY 0x0300 0x0037 %21 - uint16
      ColorPointGIntensity 0x0300 0x0038 %20 - uint8
      ColorPointBX 0x0300 0x003A %21 - uint16
      ColorPointBY 0x0300 0x003B %21 - uint16
      ColorPointBIntensity 0x0300 0x003C %20 - uint8
      EnhancedCurrentHue 0x0300 0x4000 %21 - uint16
      EnhancedColorMode 0x0300 0x4001 %30 - enum8
      ColorLoopActive 0x0300 0x4002 %20 - uint8
      ColorLoopDirection 0x0300 0x4003 %20 - uint8
      ColorLoopTime 0x0300 0x4004 %21 - uint16
      ColorLoopStartEnhancedHue 0x0300 0x4005 %21 - uint16
      ColorLoopStoredEnhancedHue 0x0300 0x4006 %21 - uint16
      ColorCapabilities 0x0300 0x400A %19 - map16
      ColorTempPhysicalMinMireds 0x0300 0x400B %21 - uint16
      ColorTempPhysicalMaxMireds 0x0300 0x400C %21 - uint16
      ColorStartUpColorTempireds 0x0300 0x4010 %21 - uint16
      BallastPhysicalMinLevel 0x0301 0x0000 %20 - uint8
      BallastPhysicalMaxLevel 0x0301 0x0001 %20 - uint8
      BallastStatus 0x0301 0x0002 %18 - map8
      BallastMinLevel 0x0301 0x0010 %20 - uint8
      BallastMaxLevel 0x0301 0x0011 %20 - uint8
      BallastPowerOnLevel 0x0301 0x0012 %20 - uint8
      BallastPowerOnFadeTime 0x0301 0x0013 %21 - uint16
      IntrinsicBallastFactor 0x0301 0x0014 %20 - uint8
      BallastFactorAdjustment 0x0301 0x0015 %20 - uint8
      BallastLampQuantity 0x0301 0x0020 %20 - uint8
      LampType 0x0301 0x0030 %42 - string
      LampManufacturer 0x0301 0x0031 %42 - string
      LampRatedHours 0x0301 0x0032 %22 - uint24
      LampBurnHours 0x0301 0x0033 %22 - uint24
      LampAlarmMode 0x0301 0x0034 %18 - map8
      LampBurnHoursTripPoint 0x0301 0x0035 %22 - uint24
      Illuminance 0x0400 0x0000 %21 - uint16
      IlluminanceMinMeasuredValue 0x0400 0x0001 %21 - uint16
      IlluminanceMaxMeasuredValue 0x0400 0x0002 %21 - uint16
      IlluminanceTolerance 0x0400 0x0003 %21 - uint16
      IlluminanceLightSensorType 0x0400 0x0004 %30 - enum8
      IlluminanceLevelStatus 0x0401 0x0000 %30 - enum8
      IlluminanceLightSensorType 0x0401 0x0001 %30 - enum8
      IlluminanceTargetLevel 0x0401 0x0010 %21 - uint16
      Temperature 0x0402 0x0000 %29 - int16
      TemperatureMinMeasuredValue 0x0402 0x0001 %29 - int16
      TemperatureMaxMeasuredValue 0x0402 0x0002 %29 - int16
      TemperatureTolerance 0x0402 0x0003 %21 - uint16
      Pressure 0x0403 0x0000 %29 - int16
      PressureMinMeasuredValue 0x0403 0x0001 %29 - int16
      PressureMaxMeasuredValue 0x0403 0x0002 %29 - int16
      PressureTolerance 0x0403 0x0003 %21 - uint16
      PressureScaledValue 0x0403 0x0010 %29 - int16
      PressureMinScaledValue 0x0403 0x0011 %29 - int16
      PressureMaxScaledValue 0x0403 0x0012 %29 - int16
      PressureScaledTolerance 0x0403 0x0013 %21 - uint16
      PressureScale 0x0403 0x0014 %28 - int8
      SeaPressure 0x0403 0xFFF0 %29 - int16
      FlowRate 0x0404 0x0000 %21 - uint16
      FlowMinMeasuredValue 0x0404 0x0001 %21 - uint16
      FlowMaxMeasuredValue 0x0404 0x0002 %21 - uint16
      FlowTolerance 0x0404 0x0003 %21 - uint16
      Humidity 0x0405 0x0000 %21 - uint16
      HumidityMinMeasuredValue 0x0405 0x0001 %21 - uint16
      HumidityMaxMeasuredValue 0x0405 0x0002 %21 - uint16
      HumidityTolerance 0x0405 0x0003 %21 - uint16
      Occupancy 0x0406 0x0000 %18 - map8
      OccupancySensorType 0x0406 0x0001 %30 - enum8
      PIROccupiedToUnoccupiedDelay 0x0406 0x0010 %21 - uint16
      PIRUnoccupiedToOccupiedDelay 0x0406 0x0011 %21 - uint16
      PIRUnoccupiedToOccupiedThreshold 0x0406 0x0012 %20 - uint8
      ZoneState 0x0500 0x0000 %30 - enum8
      ZoneType 0x0500 0x0001 %31 - enum16
      ZoneStatus 0x0500 0x0002 %19 - map16
      IASCIEAddress 0x0500 0x0010 %F0 - EUI64
      ZoneID 0x0500 0x0011 %20 - uint8
      NumberOfZoneSensitivityLevelsSupported 0x0500 0x0012 %20 - uint8
      CurrentZoneSensitivityLevel 0x0500 0x0013 %20 - uint8
      CIE 0x0500 0xFFF0 %20 - uint8
      Occupancy 0x0500 0xFFF1 %20 - uint8
      Contact 0x0500 0xFFF2 %20 - uint8
      Fire 0x0500 0xFFF3 %20 - uint8
      Water 0x0500 0xFFF4 %20 - uint8
      CO 0x0500 0xFFF5 %20 - uint8
      PersonalAlarm 0x0500 0xFFF6 %20 - uint8
      Movement 0x0500 0xFFF7 %20 - uint8
      Panic 0x0500 0xFFF8 %20 - uint8
      GlassBreak 0x0500 0xFFF9 %20 - uint8
      CurrentSummationDelivered 0x0702 0x0000 %25 - uint48
      CurrentSummationReceived 0x0702 0x0001 %25 - uint48
      CurrentMaxDemandDelivered 0x0702 0x0002 %25 - uint48
      CurrentMaxDemandReceived 0x0702 0x0003 %25 - uint48
      DFTSummation 0x0702 0x0004 %25 - uint48
      DailyFreezeTime 0x0702 0x0005 %21 - uint16
      PowerFactor 0x0702 0x0006 %28 - int8
      ReadingSnapShotTime 0x0702 0x0007 %E2 - UTC
      CurrentMaxDemandDeliveredTime 0x0702 0x0008 %E2 - UTC
      CurrentMaxDemandReceivedTime 0x0702 0x0009 %E2 - UTC
      DefaultUpdatePeriod 0x0702 0x000A %20 - uint8
      FastPollUpdatePeriod 0x0702 0x000B %20 - uint8
      CurrentBlockPeriodConsumptionDelivered 0x0702 0x000C %25 - uint48
      DailyConsumptionTarget 0x0702 0x000D %22 - uint24
      CurrentBlock 0x0702 0x000E %30 - enum8
      ProfileIntervalPeriod 0x0702 0x000F %30 - enum8
      IntervalReadReportingPeriod 0x0702 0x0010 %21 - uint16
      PresetReadingTime 0x0702 0x0011 %21 - uint16
      VolumePerReport 0x0702 0x0012 %21 - uint16
      FlowRestriction 0x0702 0x0013 %20 - uint8
      SupplyStatus 0x0702 0x0014 %30 - enum8
      CurrentInletEnergyCarrierSummation 0x0702 0x0015 %25 - uint48
      CurrentOutletEnergyCarrierSummation 0x0702 0x0016 %25 - uint48
      InletTemperature 0x0702 0x0017 %2A - int24
      OutletTemperature 0x0702 0x0018 %2A - int24
      ControlTemperature 0x0702 0x0019 %2A - int24
      CurrentInletEnergyCarrierDemand 0x0702 0x001A %2A - int24
      CurrentOutletEnergyCarrierDemand 0x0702 0x001B %2A - int24
      PreviousBlockPeriodConsumptionDelivered 0x0702 0x001C %25 - uint48
      CompanyName 0x0B01 0x0000 %42 - string
      MeterTypeID 0x0B01 0x0001 %21 - uint16
      DataQualityID 0x0B01 0x0004 %21 - uint16
      CustomerName 0x0B01 0x0005 %42 - string
      Model 0x0B01 0x0006 %41 - octstr
      PartNumber 0x0B01 0x0007 %41 - octstr
      ProductRevision 0x0B01 0x0008 %41 - octstr
      SoftwareRevision 0x0B01 0x000A %41 - octstr
      UtilityName 0x0B01 0x000B %42 - string
      POD 0x0B01 0x000C %42 - string
      AvailablePower 0x0B01 0x000D %2A - int24
      PowerThreshold 0x0B01 0x000E %2A - int24
      ElectricalMeasurementType 0x0B04 0x0000 %1B - map32
      DCVoltage 0x0B04 0x0100 %29 - int16
      DCVoltageMin 0x0B04 0x0101 %29 - int16
      DCVoltageMax 0x0B04 0x0102 %29 - int16
      DCCurrent 0x0B04 0x0103 %29 - int16
      DCCurrentMin 0x0B04 0x0104 %29 - int16
      DCCurrentMax 0x0B04 0x0105 %29 - int16
      DCPower 0x0B04 0x0106 %29 - int16
      DCPowerMin 0x0B04 0x0107 %29 - int16
      DCPowerMax 0x0B04 0x0108 %29 - int16
      DCVoltageMultiplier 0x0B04 0x0200 %21 - uint16
      DCVoltageDivisor 0x0B04 0x0201 %21 - uint16
      DCCurrentMultiplier 0x0B04 0x0202 %21 - uint16
      DCCurrentDivisor 0x0B04 0x0203 %21 - uint16
      DCPowerMultiplier 0x0B04 0x0204 %21 - uint16
      DCPowerDivisor 0x0B04 0x0205 %21 - uint16
      ACFrequency 0x0B04 0x0300 %21 - uint16
      ACFrequencyMin 0x0B04 0x0301 %21 - uint16
      ACFrequencyMax 0x0B04 0x0302 %21 - uint16
      NeutralCurrent 0x0B04 0x0303 %21 - uint16
      TotalActivePower 0x0B04 0x0304 %2B - int32
      TotalReactivePower 0x0B04 0x0305 %2B - int32
      TotalApparentPower 0x0B04 0x0306 %23 - uint32
      Measured1stHarmonicCurrent 0x0B04 0x0307 %29 - int16
      Measured3rdHarmonicCurrent 0x0B04 0x0308 %29 - int16
      Measured5thHarmonicCurrent 0x0B04 0x0309 %29 - int16
      Measured7thHarmonicCurrent 0x0B04 0x030A %29 - int16
      Measured9thHarmonicCurrent 0x0B04 0x030B %29 - int16
      Measured11thHarmonicCurrent 0x0B04 0x030C %29 - int16
      MeasuredPhase1stHarmonicCurrent 0x0B04 0x030D %29 - int16
      MeasuredPhase3rdHarmonicCurrent 0x0B04 0x030E %29 - int16
      MeasuredPhase5thHarmonicCurrent 0x0B04 0x030F %29 - int16
      MeasuredPhase7thHarmonicCurrent 0x0B04 0x0310 %29 - int16
      MeasuredPhase9thHarmonicCurrent 0x0B04 0x0311 %29 - int16
      MeasuredPhase11thHarmonicCurrent 0x0B04 0x0312 %29 - int16
      ACFrequencyMultiplier 0x0B04 0x0400 %21 - uint16
      ACFrequencyDivisor 0x0B04 0x0401 %21 - uint16
      PowerMultiplier 0x0B04 0x0402 %23 - uint32
      PowerDivisor 0x0B04 0x0403 %23 - uint32
      HarmonicCurrentMultiplier 0x0B04 0x0404 %28 - int8
      PhaseHarmonicCurrentMultiplier 0x0B04 0x0405 %28 - int8
      LineCurrent 0x0B04 0x0501 %21 - uint16
      ActiveCurrent 0x0B04 0x0502 %29 - int16
      ReactiveCurrent 0x0B04 0x0503 %29 - int16
      RMSVoltage 0x0B04 0x0505 %21 - uint16
      RMSVoltageMin 0x0B04 0x0506 %21 - uint16
      RMSVoltageMax 0x0B04 0x0507 %21 - uint16
      RMSCurrent 0x0B04 0x0508 %21 - uint16
      RMSCurrentMin 0x0B04 0x0509 %21 - uint16
      RMSCurrentMax 0x0B04 0x050A %21 - uint16
      ActivePower 0x0B04 0x050B %29 - int16
      ActivePowerMin 0x0B04 0x050C %21 - uint16
      ActivePowerMax 0x0B04 0x050D %21 - uint16
      ReactivePower 0x0B04 0x050E %29 - int16
      ApparentPower 0x0B04 0x050F %29 - int16
      PowerFactor 0x0B04 0x0510 %28 - int8
      AverageRMSVoltageMeasurementPeriod 0x0B04 0x0511 %21 - uint16
      AverageRMSOverVoltageCounter 0x0B04 0x0512 %21 - uint16
      AverageRMSUnderVoltageCounter 0x0B04 0x0513 %21 - uint16
      RMSExtremeOverVoltagePeriod 0x0B04 0x0514 %21 - uint16
      RMSExtremeUnderVoltagePeriod 0x0B04 0x0515 %21 - uint16
      RMSVoltageSagPeriod 0x0B04 0x0516 %21 - uint16
      RMSVoltageSwellPeriod 0x0B04 0x0517 %21 - uint16
      ACVoltageMultiplier 0x0B04 0x0600 %21 - uint16
      ACVoltageDivisor 0x0B04 0x0601 %21 - uint16
      ACCurrentMultiplier 0x0B04 0x0602 %21 - uint16
      ACCurrentDivisor 0x0B04 0x0603 %21 - uint16
      ACPowerMultiplier 0x0B04 0x0604 %21 - uint16
      ACPowerDivisor 0x0B04 0x0605 %21 - uint16
      DCOverloadAlarmsMask 0x0B04 0x0700 %18 - map8
      DCVoltageOverload 0x0B04 0x0701 %29 - int16
      DCCurrentOverload 0x0B04 0x0702 %29 - int16
      ACAlarmsMask 0x0B04 0x0800 %19 - map16
      ACVoltageOverload 0x0B04 0x0801 %29 - int16
      ACCurrentOverload 0x0B04 0x0802 %29 - int16
      ACActivePowerOverload 0x0B04 0x0803 %29 - int16
      ACReactivePowerOverload 0x0B04 0x0804 %29 - int16
      AverageRMSOverVoltage 0x0B04 0x0805 %29 - int16
      AverageRMSUnderVoltage 0x0B04 0x0806 %29 - int16
      RMSExtremeOverVoltage 0x0B04 0x0807 %29 - int16
      RMSExtremeUnderVoltage 0x0B04 0x0808 %29 - int16
      RMSVoltageSag 0x0B04 0x0809 %29 - int16
      RMSVoltageSwell 0x0B04 0x080A %29 - int16
      LineCurrentPhB 0x0B04 0x0901 %21 - uint16
      ActiveCurrentPhB 0x0B04 0x0902 %29 - int16
      ReactiveCurrentPhB 0x0B04 0x0903 %29 - int16
      RMSVoltagePhB 0x0B04 0x0905 %21 - uint16
      RMSVoltageMinPhB 0x0B04 0x0906 %21 - uint16
      RMSVoltageMaxPhB 0x0B04 0x0907 %21 - uint16
      RMSCurrentPhB 0x0B04 0x0908 %21 - uint16
      RMSCurrentMinPhB 0x0B04 0x0909 %21 - uint16
      RMSCurrentMaxPhB 0x0B04 0x090A %21 - uint16
      ActivePowerPhB 0x0B04 0x090B %29 - int16
      ActivePowerMinPhB 0x0B04 0x090C %29 - int16
      ActivePowerMaxPhB 0x0B04 0x090D %29 - int16
      ReactivePowerPhB 0x0B04 0x090E %29 - int16
      ApparentPowerPhB 0x0B04 0x090F %21 - uint16
      PowerFactorPhB 0x0B04 0x0910 %28 - int8
      AverageRMSVoltageMeasurementPeriodPhB 0x0B04 0x0911 %21 - uint16
      AverageRMSOverVoltageCounterPhB 0x0B04 0x0912 %21 - uint16
      AverageRMSUnderVoltageCounterPhB 0x0B04 0x0913 %21 - uint16
      RMSExtremeOverVoltagePeriodPhB 0x0B04 0x0914 %21 - uint16
      RMSExtremeUnderVoltagePeriodPhB 0x0B04 0x0915 %21 - uint16
      RMSVoltageSagPeriodPhB 0x0B04 0x0916 %21 - uint16
      RMSVoltageSwellPeriodPhB 0x0B04 0x0917 %21 - uint16
      LineCurrentPhC 0x0B04 0x0A01 %21 - uint16
      ActiveCurrentPhC 0x0B04 0x0A02 %29 - int16
      ReactiveCurrentPhC 0x0B04 0x0A03 %29 - int16
      RMSVoltagePhC 0x0B04 0x0A05 %21 - uint16
      RMSVoltageMinPhC 0x0B04 0x0A06 %21 - uint16
      RMSVoltageMaxPhC 0x0B04 0x0A07 %21 - uint16
      RMSCurrentPhC 0x0B04 0x0A08 %21 - uint16
      RMSCurrentMinPhC 0x0B04 0x0A09 %21 - uint16
      RMSCurrentMaxPhC 0x0B04 0x0A0A %21 - uint16
      ActivePowerPhC 0x0B04 0x0A0B %29 - int16
      ActivePowerMinPhC 0x0B04 0x0A0C %29 - int16
      ActivePowerMaxPhC 0x0B04 0x0A0D %29 - int16
      ReactivePowerPhC 0x0B04 0x0A0E %29 - int16
      ApparentPowerPhC 0x0B04 0x0A0F %21 - uint16
      PowerFactorPhC 0x0B04 0x0A10 %28 - int8
      AverageRMSVoltageMeasurementPeriodPhC 0x0B04 0x0A11 %21 - uint16
      AverageRMSOverVoltageCounterPhC 0x0B04 0x0A12 %21 - uint16
      AverageRMSUnderVoltageCounterPhC 0x0B04 0x0A13 %21 - uint16
      RMSExtremeOverVoltagePeriodPhC 0x0B04 0x0A14 %21 - uint16
      RMSExtremeUnderVoltagePeriodPhC 0x0B04 0x0A15 %21 - uint16
      RMSVoltageSagPeriodPhC 0x0B04 0x0A16 %21 - uint16
      RMSVoltageSwellPeriodPhC 0x0B04 0x0A17 %21 - uint16
      NumberOfResets 0x0B05 0x0000 %21 - uint16
      PersistentMemoryWrites 0x0B05 0x0001 %21 - uint16
      LastMessageLQI 0x0B05 0x011C %20 - uint8
      LastMessageRSSI 0x0B05 0x011D %20 - uint8
      LegrandOpt1 0xFC01 0x0000 %09 - data16
      LegrandOpt2 0xFC01 0x0001 %10 - bool
      LegrandOpt3 0xFC01 0x0002 %10 - bool
      LegrandHeatingMode 0xFC40 0x0000 %30 - enum8
      OppleMode 0xFCC0 0x0009 %20 - uint8
      TerncyDuration 0xFCCC 0x001A %21 - uint16
      TerncyRotate 0xFCCC 0x001B %29 - int16
      Tasmota includes plain text aliases for most of the common ZCL commands (click to expand)
      Alias Cluster Command
      Identify 0x0003 0x00
      IdentifyQuery 0x0003 0x01
      AddGroup 0x0004 0x00
      ViewGroup 0x0004 0x01
      GetGroup 0x0004 0x02
      GetAllGroups 0x0004 0x02
      RemoveGroup 0x0004 0x03
      RemoveAllGroups 0x0004 0x04
      ViewScene 0x0005 0x01
      RemoveScene 0x0005 0x02
      RemoveAllScenes 0x0005 0x03
      RecallScene 0x0005 0x05
      GetSceneMembership 0x0005 0x06
      PowerOffEffect 0x0006 0x40
      PowerOnRecall 0x0006 0x41
      PowerOnTimer 0x0006 0x42
      LidlPower 0x0006 0xFD
      Power 0x0006 0xFF
      Dimmer 0x0008 0x04
      DimmerUp 0x0008 0x06
      DimmerDown 0x0008 0x06
      DimmerStop 0x0008 0x03
      ResetAlarm 0x0009 0x00
      ResetAllAlarms 0x0009 0x01
      Hue 0x0300 0x00
      Sat 0x0300 0x03
      HueSat 0x0300 0x06
      Color 0x0300 0x07
      CT 0x0300 0x0A
      RGB 0x0300 0xF0
      ShutterOpen 0x0102 0x00
      ShutterClose 0x0102 0x01
      ShutterStop 0x0102 0x02
      ShutterLift 0x0102 0x05
      ShutterTilt 0x0102 0x08
      Shutter 0x0102 0xFF
      LegrandMode 0xFC40 0x00
      TuyaQuery 0xEF00 0x03
      TuyaMCUVersion 0xEF00 0x10
      Dimmer 0x0008 0x00
      DimmerMove 0x0008 0x01
      DimmerStepUp 0x0008 0x02
      DimmerStepDown 0x0008 0x02
      DimmerStep 0x0008 0x02
      DimmerMove 0x0008 0x05
      DimmerUp 0x0008 0x06
      DimmerDown 0x0008 0x06
      DimmerStop 0x0008 0x07
      HueMove 0x0300 0x01
      HueStepUp 0x0300 0x02
      HueStepDown 0x0300 0x02
      HueStep 0x0300 0x02
      SatMove 0x0300 0x04
      SatStep 0x0300 0x05
      ColorMove 0x0300 0x08
      ColorStep 0x0300 0x09
      ColorTempMoveUp 0x0300 0x4B
      ColorTempMoveDown 0x0300 0x4B
      ColorTempMoveStop 0x0300 0x4B
      ColorTempMove 0x0300 0x4B
      ColorTempStepUp 0x0300 0x4C
      ColorTempStepDown 0x0300 0x4C
      ColorTempStep 0x0300 0x4C
      ArrowClick 0x0005 0x07
      ArrowHold 0x0005 0x08
      ArrowRelease 0x0005 0x09
      `` 0xEF00 0xFF
      GPIdentify 0xF021 0x00
      GPScene0 0xF021 0x10
      GPScene1 0xF021 0x11
      GPScene2 0xF021 0x12
      GPScene3 0xF021 0x13
      GPScene4 0xF021 0x14
      GPScene5 0xF021 0x15
      GPScene6 0xF021 0x16
      GPScene7 0xF021 0x17
      GPScene8 0xF021 0x18
      GPScene9 0xF021 0x19
      GPScene10 0xF021 0x1A
      GPScene11 0xF021 0x1B
      GPScene12 0xF021 0x1C
      GPScene13 0xF021 0x1D
      GPScene14 0xF021 0x1E
      GPScene15 0xF021 0x1F
      GPOff 0xF021 0x20
      GPOn 0xF021 0x21
      GPToggle 0xF021 0x22
      GPRelease 0xF021 0x23
      GPMoveUp 0xF021 0x30
      GPMoveDown 0xF021 0x31
      GPStepUp 0xF021 0x32
      GPStepDown 0xF021 0x33
      GPLevelStop 0xF021 0x34
      GPMoveUpOnOff 0xF021 0x35
      GPMoveDownOnOff 0xF021 0x36
      GPStepUpOnOff 0xF021 0x37
      GPStepDownOnOff 0xF021 0x38
      GPHueStop 0xF021 0x40
      GPMoveHueUp 0xF021 0x41
      GPMoveHueDown 0xF021 0x42
      GPStepHueUp 0xF021 0x43
      GPStepHueDown 0xF021 0x44
      GPSatStop 0xF021 0x45
      GPMoveSatUp 0xF021 0x46
      GPMoveSatDown 0xF021 0x47
      GPStepSatUp 0xF021 0x48
      GPStepSatDown 0xF021 0x49
      GPMoveColor 0xF021 0x4A
      GPStepColor 0xF021 0x4B
      GPLockDoor 0xF021 0x50
      GPUnlockDoor 0xF021 0x51
      GPPress1of1 0xF021 0x60
      GPRelease1of1 0xF021 0x61
      GPPress1of2 0xF021 0x62
      GPRelease1of2 0xF021 0x63
      GPPress2of2 0xF021 0x64
      GPRelease2of2 0xF021 0x65
      GPShortPress1of1 0xF021 0x66
      GPShortPress1of2 0xF021 0x67
      GPShortPress2of2 0xF021 0x68

      Zigbee2Tasmota Status Codes~

      You can inspect the log output to determine whether Zigbee2Tasmota started correctly. Zigbee2Tasmota sends several status messages to inform the MQTT host about initialization.

      {"ZbState":{"Status":1,"Message":"CC2530 booted","RestartReason":"Watchdog","MajorRel":2,"MinorRel":6}}
      +
      • Status contains a numeric code about the status message

        Status code Description
        0 initialization complete, Zigbee2Tasmota is running normally
        1 booting
        2 resetting CC2530 configuration
        3 starting Zigbee coordinator
        20 disabling Permit Join
        21 allowing Permit Join for 60 seconds
        22 allowing Permit Join for some period
        23 Permit Join error
        30 Zigbee device connects or reconnects
        31 Received Node Descriptor information for a Zigbee device
        32 Received the list of active endpoints for a Zigbee device
        33 Received the simple Descriptor with active ZCL clusters for a Zigbee device
        34 Device announced its IEEE address
        40 Response from a device scan
        50 reporting ZNP firmware version
        51 reporting ZNP device information and associated devices
        55 reporting EZSP firmware version
        56 reporting EZSP information
        98 error, unsupported CC2530 firmware
        99 general error, Zigbee2Tasmota was unable to start
      • Message (optional) a human-readable message

      • other fields depending on the message (e.g., Status=50 or Status=51)

      Zigbee Internals~

      If you want a more technical explanation on how all this works read Zigbee-Internals

      \ No newline at end of file diff --git a/_media/A0.png b/_media/A0.png new file mode 100644 index 0000000000..196ef3d83a Binary files /dev/null and b/_media/A0.png differ diff --git a/_media/ADC_CT_circuit.png b/_media/ADC_CT_circuit.png new file mode 100644 index 0000000000..78106aa643 Binary files /dev/null and b/_media/ADC_CT_circuit.png differ diff --git a/_media/ADCesp32.png b/_media/ADCesp32.png new file mode 100644 index 0000000000..d2fc151135 Binary files /dev/null and b/_media/ADCesp32.png differ diff --git a/_media/Analog0.png b/_media/Analog0.png new file mode 100644 index 0000000000..144c83a454 Binary files /dev/null and b/_media/Analog0.png differ diff --git a/_media/ArduinoIDE_settings.png b/_media/ArduinoIDE_settings.png new file mode 100644 index 0000000000..bb1c7c5185 Binary files /dev/null and b/_media/ArduinoIDE_settings.png differ diff --git a/_media/BME280-Tasmota-GPIO-Setting.png b/_media/BME280-Tasmota-GPIO-Setting.png new file mode 100644 index 0000000000..b584d563f2 Binary files /dev/null and b/_media/BME280-Tasmota-GPIO-Setting.png differ diff --git a/_media/Blitzwolf_SHP6_15A.jpg b/_media/Blitzwolf_SHP6_15A.jpg new file mode 100644 index 0000000000..ad92316a41 Binary files /dev/null and b/_media/Blitzwolf_SHP6_15A.jpg differ diff --git a/_media/CC2530 External Antenna.png b/_media/CC2530 External Antenna.png new file mode 100644 index 0000000000..8e48ce1724 Binary files /dev/null and b/_media/CC2530 External Antenna.png differ diff --git a/_media/DY-SV17F_datasheet.pdf b/_media/DY-SV17F_datasheet.pdf new file mode 100644 index 0000000000..ce2248fc04 Binary files /dev/null and b/_media/DY-SV17F_datasheet.pdf differ diff --git a/_media/ESP-01-Pin-Out.png b/_media/ESP-01-Pin-Out.png new file mode 100644 index 0000000000..8ae8a66fa0 Binary files /dev/null and b/_media/ESP-01-Pin-Out.png differ diff --git a/_media/GPIO13-switch.png b/_media/GPIO13-switch.png new file mode 100644 index 0000000000..b28920761f Binary files /dev/null and b/_media/GPIO13-switch.png differ diff --git a/_media/MAX7219-tasmota-config.png b/_media/MAX7219-tasmota-config.png new file mode 100644 index 0000000000..5926f11396 Binary files /dev/null and b/_media/MAX7219-tasmota-config.png differ diff --git a/_media/OptoSerial.jpg b/_media/OptoSerial.jpg new file mode 100644 index 0000000000..5b26dbb82d Binary files /dev/null and b/_media/OptoSerial.jpg differ diff --git a/_media/Partition_Wizard.png b/_media/Partition_Wizard.png new file mode 100644 index 0000000000..b3727ed0d8 Binary files /dev/null and b/_media/Partition_Wizard.png differ diff --git a/_media/Philips_motion_sensor_SML001.jpg b/_media/Philips_motion_sensor_SML001.jpg new file mode 100644 index 0000000000..89762d1e1f Binary files /dev/null and b/_media/Philips_motion_sensor_SML001.jpg differ diff --git a/_media/Resol_VBus_adaptor_to_WemosD1Mini.png b/_media/Resol_VBus_adaptor_to_WemosD1Mini.png new file mode 100644 index 0000000000..8e8f941acb Binary files /dev/null and b/_media/Resol_VBus_adaptor_to_WemosD1Mini.png differ diff --git a/_media/SWB1-COM2.jpg b/_media/SWB1-COM2.jpg new file mode 100644 index 0000000000..bd33af34de Binary files /dev/null and b/_media/SWB1-COM2.jpg differ diff --git a/_media/TM1637-tasmota-config.png b/_media/TM1637-tasmota-config.png new file mode 100644 index 0000000000..e76b269c39 Binary files /dev/null and b/_media/TM1637-tasmota-config.png differ diff --git a/_media/TM1638-tasmota-config.png b/_media/TM1638-tasmota-config.png new file mode 100644 index 0000000000..979256b517 Binary files /dev/null and b/_media/TM1638-tasmota-config.png differ diff --git a/_media/TM1650-tasmota-config.png b/_media/TM1650-tasmota-config.png new file mode 100644 index 0000000000..2b76d30cc7 Binary files /dev/null and b/_media/TM1650-tasmota-config.png differ diff --git a/_media/TYWE3S_fullpinout.png b/_media/TYWE3S_fullpinout.png new file mode 100644 index 0000000000..0f19f56608 Binary files /dev/null and b/_media/TYWE3S_fullpinout.png differ diff --git a/_media/TYWE3S_pinout.png b/_media/TYWE3S_pinout.png new file mode 100644 index 0000000000..c84d97b33e Binary files /dev/null and b/_media/TYWE3S_pinout.png differ diff --git a/_media/TasUI.png b/_media/TasUI.png new file mode 100644 index 0000000000..b7e54a5b09 Binary files /dev/null and b/_media/TasUI.png differ diff --git a/_media/aqara.png b/_media/aqara.png new file mode 100644 index 0000000000..c995d424f8 Binary files /dev/null and b/_media/aqara.png differ diff --git a/_media/artnet/LED_Lab_Menu1.png b/_media/artnet/LED_Lab_Menu1.png new file mode 100644 index 0000000000..d243c7c4e6 Binary files /dev/null and b/_media/artnet/LED_Lab_Menu1.png differ diff --git a/_media/artnet/LED_Lab_Menu2.png b/_media/artnet/LED_Lab_Menu2.png new file mode 100644 index 0000000000..a214131ddb Binary files /dev/null and b/_media/artnet/LED_Lab_Menu2.png differ diff --git a/_media/artnet/LED_Lab_Setup.png b/_media/artnet/LED_Lab_Setup.png new file mode 100644 index 0000000000..2243d4ce28 Binary files /dev/null and b/_media/artnet/LED_Lab_Setup.png differ diff --git a/_media/aws_iot/Tasmota-MqttPolicy.yaml b/_media/aws_iot/Tasmota-MqttPolicy.yaml new file mode 100644 index 0000000000..307275bddb --- /dev/null +++ b/_media/aws_iot/Tasmota-MqttPolicy.yaml @@ -0,0 +1,65 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: "Tasmota: create the AWS IoT policy to be used by all Tasmota devices. This policy needs to be created once per AWS Region for all Tasmota devices." + +Parameters: + RetentionPolicy: + Type: String + AllowedValues: [ "Delete", "Retain" ] + Default: "Retain" + Description: When you delete the CloudFormation template, "Delete" will also delete this policy, "Retain" will keep it + MQTTPolicyName: + Type: String + Default: "TasmotaMqttPolicy" + Description: The Name of the MQTTPolicy for Tasmota devices (leave unchanged unless you have specific needs) + +Outputs: + MqttPolicyName: + Description: The name of the AWS IoT policy created + Value: !If [ Retained, !Ref TasmotaMqttPolicyRetained, !Ref TasmotaMqttPolicyNotRetained ] + +Conditions: + Retained: !Equals [ !Ref RetentionPolicy, "Retain" ] + NotRetained: !Not [ !Equals [ !Ref RetentionPolicy, "Retain" ] ] + +Resources: + + ###################################################################### + # + # The AWS IoT policy to be used by all Tasmota devices. + # It needs to be created once for all. + # This policy will remain if you delete the CloudFormation template + # + ###################################################################### + TasmotaMqttPolicyRetained: + Type: AWS::IoT::Policy + Condition: Retained + DeletionPolicy: Retain + Properties: + PolicyDocument: + Version: "2012-10-17" + Statement: + - + Effect: Allow + Action: [ "iot:Connect", "iot:Publish", "iot:Subscribe", "iot:Receive" ] + Resource: !Sub "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:*" + PolicyName: !Ref MQTTPolicyName + + ###################################################################### + # + # This policy will be removed if you delete the CloudFormation template + # + ###################################################################### + TasmotaMqttPolicyNotRetained: + Type: AWS::IoT::Policy + Condition: NotRetained + DeletionPolicy: Delete + Properties: + PolicyDocument: + Version: "2012-10-17" + Statement: + - + Effect: Allow + Action: [ "iot:Connect", "iot:Publish", "iot:Subscribe", "iot:Receive" ] + Resource: !Sub "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:*" + PolicyName: !Ref MQTTPolicyName + diff --git a/_media/aws_iot/Tasmota-Thing.yaml b/_media/aws_iot/Tasmota-Thing.yaml new file mode 100644 index 0000000000..bedc13d58a --- /dev/null +++ b/_media/aws_iot/Tasmota-Thing.yaml @@ -0,0 +1,287 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: "Tasmota: create an AWS IoT Thing for a Tasmota device. This template will create the Private Key for the Thing, sign the certificate and generate the Tasmota commands. This template must be used for each Tasmota device you want to connect to AWS IoT." + +Parameters: + ThingNameParam: + Type: String + Default: "" + Description: Name of the AWS IoT thing you want to create (leave black to default to the CloudFormation stack name) + RetentionPolicy: + Type: String + AllowedValues: [ "Delete", "Retain" ] + Default: "Retain" + Description: When you delete the CloudFormation template, "Delete" will also delete this policy, "Retain" will keep it + MQTTPolicyName: + Type: String + Default: "TasmotaMqttPolicy" + Description: Name of the MQTTPolicy for Tasmota devices (leave unchanged unless you have specific needs) + +Outputs: + ThingName: + Description: The name of the AWS IoT thing created + Value: !If [ Retained, !Ref TasmotaThingRetained, !Ref TasmotaThingNotRetained ] + TlsKey1: + Description: TlsKey1 command to configure the private key in Tasmota + Value: !GetAtt TasmotaGeneratePrivKeyAndCert.tls1 + TlsKey2: + Description: TlsKey2 command to configure the certiicate in Tasmota + Value: !GetAtt CertificateGetPEM.tls2 + MqttHost: + Description: MqttHost command to configure Tasmota + Value: !GetAtt CertificateGetPEM.endpoint + +Conditions: + DefaultThingName: !Equals ["", !Ref ThingNameParam] + Retained: !Equals [ !Ref RetentionPolicy, "Retain" ] + NotRetained: !Not [ !Equals [ !Ref RetentionPolicy, "Retain" ] ] + + +Resources: + + ###################################################################### + # + # The target AWS IoT Thing + # This Thing will remain if you delete the CloudFormation template + # + ###################################################################### + TasmotaThingRetained: + Type: AWS::IoT::Thing + Condition: Retained + DeletionPolicy: Retain + Properties: + ThingName: !If [ DefaultThingName, !Ref "AWS::StackName", !Ref ThingNameParam ] + TasmotaThingNotRetained: + Type: AWS::IoT::Thing + Condition: NotRetained + DeletionPolicy: Delete + Properties: + ThingName: !If [ DefaultThingName, !Ref "AWS::StackName", !Ref ThingNameParam ] + + ###################################################################### + # + # Execution Role Policy required by the AWS Lambda function + # to create the required AWS IoT objects. It is not used after + # initial creation. + # + ###################################################################### + LambdaExecRoleProvisioning: + Type: "AWS::IAM::Role" + Properties: + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + Policies: + - + PolicyName: AWSIotProvisioning + PolicyDocument: + Version: "2012-10-17" + Statement: + - + Effect: Allow + Action: [ "iot:*" ] + Resource: "*" + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - + Sid: "" + Effect: Allow + Principal: + Service: "lambda.amazonaws.com" + Action: "sts:AssumeRole" + + ###################################################################### + # + # The Certificate used by the AWS IoT Thing to authenticate + # + ###################################################################### + TasmotaCertificateRetained: + Type: AWS::IoT::Certificate + Condition: Retained + DeletionPolicy: Retain + Properties: + CertificateSigningRequest: !GetAtt TasmotaGeneratePrivKeyAndCert.csr + Status: "ACTIVE" + TasmotaCertificateNotRetained: + Type: AWS::IoT::Certificate + Condition: NotRetained + DeletionPolicy: Delete + Properties: + CertificateSigningRequest: !GetAtt TasmotaGeneratePrivKeyAndCert.csr + Status: "ACTIVE" + + ###################################################################### + # + # Attachment between the Certificate and the Policy + # + ###################################################################### + TasmotaPolicyCertificateAttachmentRetained: + Type: AWS::IoT::PolicyPrincipalAttachment + Condition: Retained + DeletionPolicy: Retain + Properties: + PolicyName: !Ref MQTTPolicyName + Principal: !GetAtt TasmotaCertificateRetained.Arn + TasmotaPolicyCertificateAttachmentNotRetained: + Type: AWS::IoT::PolicyPrincipalAttachment + Condition: NotRetained + DeletionPolicy: Delete + Properties: + PolicyName: !Ref MQTTPolicyName + Principal: !GetAtt TasmotaCertificateNotRetained.Arn + + ###################################################################### + # + # Attachment between the Certificate and the Thing + # + ###################################################################### + TasmotaThingCertificateAttachmentRetained: + Type: AWS::IoT::ThingPrincipalAttachment + Condition: Retained + DeletionPolicy: Retain + Properties: + ThingName: !Ref TasmotaThingRetained + Principal: !GetAtt TasmotaCertificateRetained.Arn + TasmotaThingCertificateAttachmentNotRetained: + Type: AWS::IoT::ThingPrincipalAttachment + Condition: NotRetained + DeletionPolicy: Delete + Properties: + ThingName: !Ref TasmotaThingNotRetained + Principal: !GetAtt TasmotaCertificateNotRetained.Arn + + ###################################################################### + # + # Custom resource used to generate the Thing private and CSR + # + ###################################################################### + TasmotaGeneratePrivKeyAndCert: + Type: "Custom::TasmotaGeneratePrivKeyAndCertLambda" + Version: "1.0" + Properties: + ServiceToken: !GetAtt TasmotaGeneratePrivKeyAndCertLambda.Arn + + TasmotaGeneratePrivKeyAndCertLambda: + Type: "AWS::Lambda::Function" + Properties: + Handler: "index.handler" + MemorySize: 512 + Role: !GetAtt LambdaExecRoleProvisioning.Arn + Runtime: python3.7 + Timeout: 5 + Code: + ZipFile: | + import cfnresponse + import traceback + import json + import os + import codecs + + def handler(event, context): + try: + # generate ec 256 bits private key + cmd = 'openssl ecparam -name prime256v1 -genkey -out /tmp/tasmota-01.key'; + ret = os.system(cmd) + if ret != 0: + raise Exception('Error generating private key '+ret) + + with open('/tmp/tasmota-01.key', 'r') as tasmota_key_file: + tasmota_key = tasmota_key_file.read() + + cmd = "openssl ec -in /tmp/tasmota-01.key -inform PEM -outform DER | openssl asn1parse -inform DER | head -3 | tail -1 | awk -F':' '{ print $4 }' > /tmp/tls1.hex" + ret = os.system(cmd) + if ret != 0: + raise Exception('Error generating TLSKey1 command '+ret) + + with open('/tmp/tls1.hex', 'r') as tls1file: + tls1hex = tls1file.read().rstrip() + + tls1cmd = "TLSKey1 " + codecs.encode(codecs.decode(tls1hex, 'hex'), 'base64').decode().rstrip() + + # generate CSR + cmd = "openssl req -new -sha256 -key /tmp/tasmota-01.key -nodes -out /tmp/tasmota-01.csr -subj '/C=EU' -days 3650" + ret = os.system(cmd) + if ret != 0: + raise Exception('Error generating CSR '+ret) + + with open('/tmp/tasmota-01.csr', 'r') as csrfile: + csr = csrfile.read().rstrip() + + except Exception as e: + print(traceback.format_exc()) + cfnresponse.send(event, context, cfnresponse.FAILED, { "error": str(e) }) + else: + cfnresponse.send(event, context, cfnresponse.SUCCESS,{ + 'Result': 'Ok', + 'privkey': tasmota_key, + 'tls1': tls1cmd, + 'csr': csr + }) + + ###################################################################### + # + # Custom resource used to convert the signed certificate into + # the target Tlskey2 Tasmota command + # + ###################################################################### + CertificateGetPEM: + Type: "Custom::CertificateGetPEMLambda" + Version: "1.0" + Properties: + ServiceToken: !GetAtt CertificateGetPEMLambda.Arn + certID: !If [ Retained, !Ref TasmotaCertificateRetained, !Ref TasmotaCertificateNotRetained ] + + CertificateGetPEMLambda: + Type: "AWS::Lambda::Function" + Properties: + Handler: "index.handler" + MemorySize: 512 + Role: !GetAtt LambdaExecRoleProvisioning.Arn + Runtime: python3.7 + Timeout: 5 + Code: + ZipFile: | + import cfnresponse + import traceback + import json + import boto3 + import os + + def handler(event, context): + try: + certID = event['ResourceProperties']['certID'] + + client = boto3.client('iot') + + # get the ATS endpoint + response = client.describe_endpoint(endpointType='iot:Data-ATS') + ats_endpoint = response['endpointAddress'] + + # create the full Tasmota command + endpoint = f"Backlog MqttHost {ats_endpoint}; MqttPort 8883" + + # get the PEM + response = client.describe_certificate(certificateId=certID) + pem = response['certificateDescription']['certificatePem'] + with open('/tmp/tasmota-01.cert.pem', 'w') as pemfile: + pemfile.write(pem) + + cmd = 'openssl x509 -in /tmp/tasmota-01.cert.pem -inform PEM -outform DER | base64 > /tmp/tls2.b64' + ret = os.system(cmd) + if ret != 0: + raise Exception('Error generating certificate base64 '+ret) + + with open('/tmp/tls2.b64', 'r') as tls2file: + tls2b64 = tls2file.read().rstrip() + + tls2cmd = "TLSKey2 " + tls2b64 + + except Exception as e: + print(traceback.format_exc()) + cfnresponse.send(event, context, cfnresponse.FAILED, { "error": str(e) }) + else: + cfnresponse.send(event, context, cfnresponse.SUCCESS,{ + 'Result': 'Ok', + 'endpoint': endpoint, + 'pem': pem, + 'tls2': tls2cmd + }) diff --git a/_media/aws_iot/TasmotaAuth.yaml b/_media/aws_iot/TasmotaAuth.yaml new file mode 100644 index 0000000000..bc5da2e0b7 --- /dev/null +++ b/_media/aws_iot/TasmotaAuth.yaml @@ -0,0 +1,337 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: "Tasmota: create an AWS IoT authorizer for user/password authentication of Tasmota devices over TLS." + +Parameters: + RetentionPolicy: + Type: String + AllowedValues: [ "Delete", "Retain" ] + Default: "Retain" + Description: When you delete the CloudFormation template, "Delete" will all objects, "Retain" will keep it what is necessary for Tasmota to authenticate (you can later delete manually the Lambda function and the IOA Custom Authorizer) + MQTTAuthorizerName: + Type: String + Default: "TasmotaAuth" + Description: The Name of the authorizer that will need to be added in the user name for Tasmota devices (leave unchanged unless you have specific needs) + +Outputs: + MQTTUser: + Description: The user to be used by Tasmota devices + Value: !Sub "tasmota?x-amz-customauthorizer-name=${MQTTAuthorizerName}" + MQTTPassword: + Description: The password to be used by Tasmota devices + Value: !GetAtt GeneratePassword.Password + BackLogCommand: + Description: The complete `backlog` command to configure the Tasmota device + Value: !Sub "BackLog SetOption3 1; SetOption103 1; MqttHost ${GeneratePassword.Endpoint}; MqttPort 443; MqttUser tasmota?x-amz-customauthorizer-name=${MQTTAuthorizerName}; MqttPassword ${GeneratePassword.Password}" + +Conditions: + Retained: !Equals [ !Ref RetentionPolicy, "Retain" ] + NotRetained: !Not [ !Equals [ !Ref RetentionPolicy, "Retain" ] ] + +Resources: + + ###################################################################### + # + # Generate a truly random strong password + # using hardware high-entropy random generators + # + ###################################################################### + GeneratePassword: + Type: "Custom::GeneratePassword" + Properties: + ServiceToken: !GetAtt LambdaGeneratePassword.Arn + + # Lambda function invoking the content Lambda function and stores in S3 + LambdaGeneratePassword: + Type: "AWS::Lambda::Function" + Properties: + Handler: "index.handler" + Description: "Tasmota: temporary function used to generate a random password (can be deleted safely)" + MemorySize: 256 + Role: !GetAtt LambdaBasicExecRoleRandomGenerator.Arn + Runtime: python3.7 + Timeout: 10 + Code: + ZipFile: | + import cfnresponse + import traceback + import boto3 + import base64 + + def handler(event, context): + try: + # generate random password + kms = boto3.client('kms') + response = kms.generate_random(NumberOfBytes=16) + bytes = response['Plaintext'] + password = base64.b64encode(bytes).decode('utf-8') + # get endpoint + iot = boto3.client('iot') + response = iot.describe_endpoint(endpointType='iot:Data-ATS') + ats_endpoint = response['endpointAddress'] + # return results + cfnresponse.send(event, context, cfnresponse.SUCCESS, { "Password": password, "Endpoint": ats_endpoint }) + except Exception as e: + print(traceback.format_exc()) + cfnresponse.send(event, context, cfnresponse.FAILED, { "error": str(e) }) + + # Generic role for AWS Lamdda functions + LambdaBasicExecRoleRandomGenerator: + Type: "AWS::IAM::Role" + Properties: + Policies: + - + PolicyName: GenerateRandom + PolicyDocument: + Version: "2012-10-17" + Statement: + - + Effect: Allow + Action: [ "kms:GenerateRandom", "iot:DescribeEndpoint"] + Resource: "*" + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - + Sid: "" + Effect: Allow + Principal: + Service: "lambda.amazonaws.com" + Action: "sts:AssumeRole" + + ###################################################################### + # + # Lambda function for authorizer + # Accepts two passwords as ENV variables + # + ###################################################################### + # Lambda function invoking the content Lambda function and stores in S3 + LambdaIOTAuthorizer: + Type: "AWS::Lambda::Function" + Condition: NotRetained + Properties: + Handler: "index.lambda_handler" + Description: "Tasmota: custom authorizer for Tasmota MQTT using password credentials" + MemorySize: 128 + Role: !GetAtt LambdaBasicExecRole.Arn + Runtime: python3.7 + Timeout: 2 + Environment: + Variables: + PASSWD: !GetAtt GeneratePassword.Password + Code: + ZipFile: | + import json + import base64 + import os + + PASSWD = os.environ.get('PASSWD', "") + PASSWD2 = os.environ.get('PASSWD2', "") + + # https://stackoverflow.com/questions/3405073/generating-dictionary-keys-on-the-fly/3405143#3405143 + class D(dict): + def __missing__(self, key): + self[key] = D() + return self[key] + + def lambda_handler(event, context): + # print("event="+json.dumps(event)) + event = D(event) # make it safe to dereference + + uname = event['protocolData']['mqtt']['username'] or "" + pwd = event['protocolData']['mqtt']['password'] or "" + passwd = base64.b64decode(pwd).decode('utf-8') + client_id = event['protocolData']['mqtt']['clientId'] or "" + + passwd_ok = False + if (PASSWD) and (passwd == PASSWD): passwd_ok = True + if (PASSWD2) and (passwd == PASSWD2): passwd_ok = True + + # print("passwd = {}, PASSWD = {}, passwd_ok = {}".format(passwd, PASSWD, passwd_ok)) + + if passwd_ok: + return generateAuthResponse(client_id, 'Allow') + else: + return generateAuthResponse(client_id, 'Deny') + + def generateAuthResponse(token, effect): + auth_response = { + 'isAuthenticated': True, + 'principalId': 'Tasmota', + 'disconnectAfterInSeconds': 86400, + 'refreshAfterInSeconds': 86400, + 'policyDocuments': [ + { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Action': [ + 'iot:Connect', + 'iot:Publish', + 'iot:Subscribe', + 'iot:Receive', + ], + 'Effect': effect, + 'Resource': '*' + } + ] + } + ] + } + # print("auth_response = "+json.dumps(auth_response)) + return auth_response + + LambdaIOTAuthorizerRetain: + Type: "AWS::Lambda::Function" + Condition: Retained + DeletionPolicy: Retain + Properties: + Handler: "index.lambda_handler" + Description: "Tasmota: custom authorizer for Tasmota MQTT using password credentials" + MemorySize: 128 + Role: !GetAtt LambdaBasicExecRoleRetain.Arn + Runtime: python3.7 + Timeout: 2 + Environment: + Variables: + PASSWD: !GetAtt GeneratePassword.Password + Code: + ZipFile: | + import json + import base64 + import os + + PASSWD = os.environ.get('PASSWD', "") + PASSWD2 = os.environ.get('PASSWD2', "") + + # https://stackoverflow.com/questions/3405073/generating-dictionary-keys-on-the-fly/3405143#3405143 + class D(dict): + def __missing__(self, key): + self[key] = D() + return self[key] + + def lambda_handler(event, context): + # print("event="+json.dumps(event)) + event = D(event) # make it safe to dereference + + uname = event['protocolData']['mqtt']['username'] or "" + pwd = event['protocolData']['mqtt']['password'] or "" + passwd = base64.b64decode(pwd).decode('utf-8') + client_id = event['protocolData']['mqtt']['clientId'] or "" + + passwd_ok = False + if (PASSWD) and (passwd == PASSWD): passwd_ok = True + if (PASSWD2) and (passwd == PASSWD2): passwd_ok = True + + # print("passwd = {}, PASSWD = {}, passwd_ok = {}".format(passwd, PASSWD, passwd_ok)) + + if passwd_ok: + return generateAuthResponse(client_id, 'Allow') + else: + return generateAuthResponse(client_id, 'Deny') + + def generateAuthResponse(token, effect): + auth_response = { + 'isAuthenticated': True, + 'principalId': 'Tasmota', + 'disconnectAfterInSeconds': 86400, + 'refreshAfterInSeconds': 86400, + 'policyDocuments': [ + { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Action': [ + 'iot:Connect', + 'iot:Publish', + 'iot:Subscribe', + 'iot:Receive', + ], + 'Effect': effect, + 'Resource': '*' + } + ] + } + ] + } + # print("auth_response = "+json.dumps(auth_response)) + return auth_response + # Generic role for AWS Lamdda functions + LambdaBasicExecRole: + Type: "AWS::IAM::Role" + Condition: NotRetained + Properties: + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - + Sid: "" + Effect: Allow + Principal: + Service: "lambda.amazonaws.com" + Action: "sts:AssumeRole" + + LambdaBasicExecRoleRetain: + Type: "AWS::IAM::Role" + Condition: Retained + DeletionPolicy: Retain + Properties: + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - + Sid: "" + Effect: Allow + Principal: + Service: "lambda.amazonaws.com" + Action: "sts:AssumeRole" + + # Invoke permission for AWS IoT + LambdaAuthorizerInvokePermission: + Type: "AWS::Lambda::Permission" + Condition: NotRetained + Properties: + FunctionName: !GetAtt LambdaIOTAuthorizer.Arn + Action: "lambda:InvokeFunction" + Principal: "iot.amazonaws.com" + SourceArn: !Sub "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:authorizer/${MQTTAuthorizerName}" + + LambdaAuthorizerInvokePermissionRetain: + Type: "AWS::Lambda::Permission" + Condition: Retained + DeletionPolicy: Retain + Properties: + FunctionName: !GetAtt LambdaIOTAuthorizerRetain.Arn + Action: "lambda:InvokeFunction" + Principal: "iot.amazonaws.com" + SourceArn: !Sub "arn:aws:iot:${AWS::Region}:${AWS::AccountId}:authorizer/${MQTTAuthorizerName}" + + ###################################################################### + # + # Custom authorizer + # + ###################################################################### + TasmotaCustomAuthorizer: + Type: "AWS::IoT::Authorizer" + Condition: NotRetained + Properties: + AuthorizerFunctionArn: !GetAtt LambdaIOTAuthorizer.Arn + AuthorizerName: !Ref MQTTAuthorizerName + SigningDisabled: True + Status: ACTIVE + # Retained version + TasmotaCustomAuthorizerRetain: + Type: "AWS::IoT::Authorizer" + Condition: Retained + DeletionPolicy: Retain + Properties: + AuthorizerFunctionArn: !GetAtt LambdaIOTAuthorizerRetain.Arn + AuthorizerName: !Ref MQTTAuthorizerName + SigningDisabled: True + Status: ACTIVE + diff --git a/_media/berry/berry.svg b/_media/berry/berry.svg new file mode 100644 index 0000000000..d76b273e64 --- /dev/null +++ b/_media/berry/berry.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + diff --git a/_media/bg.jpg b/_media/bg.jpg new file mode 100644 index 0000000000..b4a2277857 Binary files /dev/null and b/_media/bg.jpg differ diff --git a/_media/bg.png b/_media/bg.png new file mode 100644 index 0000000000..5061f3f682 Binary files /dev/null and b/_media/bg.png differ diff --git a/_media/bluetooth/CGD1.png b/_media/bluetooth/CGD1.png new file mode 100644 index 0000000000..648b8c2f50 Binary files /dev/null and b/_media/bluetooth/CGD1.png differ diff --git a/_media/bluetooth/CGG1.png b/_media/bluetooth/CGG1.png new file mode 100644 index 0000000000..d395121c01 Binary files /dev/null and b/_media/bluetooth/CGG1.png differ diff --git a/_media/bluetooth/LYWDS02.png b/_media/bluetooth/LYWDS02.png new file mode 100644 index 0000000000..698924d629 Binary files /dev/null and b/_media/bluetooth/LYWDS02.png differ diff --git a/_media/bluetooth/LYWSD03MMC.png b/_media/bluetooth/LYWSD03MMC.png new file mode 100644 index 0000000000..9ad7a9ffd3 Binary files /dev/null and b/_media/bluetooth/LYWSD03MMC.png differ diff --git a/_media/bluetooth/MCCGQ02HL.png b/_media/bluetooth/MCCGQ02HL.png new file mode 100644 index 0000000000..ca6447a77a Binary files /dev/null and b/_media/bluetooth/MCCGQ02HL.png differ diff --git a/_media/bluetooth/MHO-C303.png b/_media/bluetooth/MHO-C303.png new file mode 100644 index 0000000000..340e93e2c9 Binary files /dev/null and b/_media/bluetooth/MHO-C303.png differ diff --git a/_media/bluetooth/MHO-C401.png b/_media/bluetooth/MHO-C401.png new file mode 100644 index 0000000000..29fa9ec059 Binary files /dev/null and b/_media/bluetooth/MHO-C401.png differ diff --git a/_media/bluetooth/SJWS01L.png b/_media/bluetooth/SJWS01L.png new file mode 100644 index 0000000000..8e2a26d713 Binary files /dev/null and b/_media/bluetooth/SJWS01L.png differ diff --git a/_media/bluetooth/YLKG08.png b/_media/bluetooth/YLKG08.png new file mode 100644 index 0000000000..4a490ff26d Binary files /dev/null and b/_media/bluetooth/YLKG08.png differ diff --git a/_media/bluetooth/itag.png b/_media/bluetooth/itag.png new file mode 100644 index 0000000000..4ec1970806 Binary files /dev/null and b/_media/bluetooth/itag.png differ diff --git a/_media/bluetooth/itag2.png b/_media/bluetooth/itag2.png new file mode 100644 index 0000000000..c13802933e Binary files /dev/null and b/_media/bluetooth/itag2.png differ diff --git a/_media/bluetooth/itag3.png b/_media/bluetooth/itag3.png new file mode 100644 index 0000000000..db1e0a035a Binary files /dev/null and b/_media/bluetooth/itag3.png differ diff --git a/_media/bluetooth/miflora.png b/_media/bluetooth/miflora.png new file mode 100644 index 0000000000..59bb8d13e7 Binary files /dev/null and b/_media/bluetooth/miflora.png differ diff --git a/_media/bluetooth/mj_ht_v1.png b/_media/bluetooth/mj_ht_v1.png new file mode 100644 index 0000000000..a167b45fc7 Binary files /dev/null and b/_media/bluetooth/mj_ht_v1.png differ diff --git a/_media/bluetooth/mjyd2s.jpg b/_media/bluetooth/mjyd2s.jpg new file mode 100644 index 0000000000..bc1df574ce Binary files /dev/null and b/_media/bluetooth/mjyd2s.jpg differ diff --git a/_media/bluetooth/mjyd2s.png b/_media/bluetooth/mjyd2s.png new file mode 100644 index 0000000000..a6cc8ba66c Binary files /dev/null and b/_media/bluetooth/mjyd2s.png differ diff --git a/_media/bluetooth/nRF51822.png b/_media/bluetooth/nRF51822.png new file mode 100644 index 0000000000..7812a5641c Binary files /dev/null and b/_media/bluetooth/nRF51822.png differ diff --git a/_media/bluetooth/nlight.jpg b/_media/bluetooth/nlight.jpg new file mode 100644 index 0000000000..826d1fec8a Binary files /dev/null and b/_media/bluetooth/nlight.jpg differ diff --git a/_media/bluetooth/nlight.png b/_media/bluetooth/nlight.png new file mode 100644 index 0000000000..f83fdc1e71 Binary files /dev/null and b/_media/bluetooth/nlight.png differ diff --git a/_media/bluetooth/rBVaI1ilQ_2ADqc8AADetI8fy2Q733.jpg b/_media/bluetooth/rBVaI1ilQ_2ADqc8AADetI8fy2Q733.jpg new file mode 100644 index 0000000000..2c81498e6b Binary files /dev/null and b/_media/bluetooth/rBVaI1ilQ_2ADqc8AADetI8fy2Q733.jpg differ diff --git a/_media/bluetooth/yeerc.jpg b/_media/bluetooth/yeerc.jpg new file mode 100644 index 0000000000..73b03ceb75 Binary files /dev/null and b/_media/bluetooth/yeerc.jpg differ diff --git a/_media/bluetooth/yeerc.png b/_media/bluetooth/yeerc.png new file mode 100644 index 0000000000..ceb86b4b86 Binary files /dev/null and b/_media/bluetooth/yeerc.png differ diff --git a/_media/button-matrix.png b/_media/button-matrix.png new file mode 100644 index 0000000000..184d53789a Binary files /dev/null and b/_media/button-matrix.png differ diff --git a/_media/ccloader.png b/_media/ccloader.png new file mode 100644 index 0000000000..8443c60112 Binary files /dev/null and b/_media/ccloader.png differ diff --git a/_media/ch340g.png b/_media/ch340g.png new file mode 100644 index 0000000000..6aa76b8fc7 Binary files /dev/null and b/_media/ch340g.png differ diff --git a/_media/components.png b/_media/components.png new file mode 100644 index 0000000000..e2302dc90f Binary files /dev/null and b/_media/components.png differ diff --git a/_media/components2.png b/_media/components2.png new file mode 100644 index 0000000000..566c105e41 Binary files /dev/null and b/_media/components2.png differ diff --git a/_media/datasheets/esp32-c3-mini-1_datasheet_en.pdf b/_media/datasheets/esp32-c3-mini-1_datasheet_en.pdf new file mode 100644 index 0000000000..49b9abd871 Binary files /dev/null and b/_media/datasheets/esp32-c3-mini-1_datasheet_en.pdf differ diff --git a/_media/datasheets/esp32-c3-wroom-02_datasheet_en.pdf b/_media/datasheets/esp32-c3-wroom-02_datasheet_en.pdf new file mode 100644 index 0000000000..c0492fab93 Binary files /dev/null and b/_media/datasheets/esp32-c3-wroom-02_datasheet_en.pdf differ diff --git a/_media/datasheets/esp32-pico-d4_datasheet_en.pdf b/_media/datasheets/esp32-pico-d4_datasheet_en.pdf new file mode 100644 index 0000000000..2b49b6bad8 Binary files /dev/null and b/_media/datasheets/esp32-pico-d4_datasheet_en.pdf differ diff --git a/_media/datasheets/esp32-pico-v3-02_datasheet_en.pdf b/_media/datasheets/esp32-pico-v3-02_datasheet_en.pdf new file mode 100644 index 0000000000..93ca4b7d88 Binary files /dev/null and b/_media/datasheets/esp32-pico-v3-02_datasheet_en.pdf differ diff --git a/_media/datasheets/esp32-pico-v3_datasheet_en.pdf b/_media/datasheets/esp32-pico-v3_datasheet_en.pdf new file mode 100644 index 0000000000..6fd244017c Binary files /dev/null and b/_media/datasheets/esp32-pico-v3_datasheet_en.pdf differ diff --git a/_media/datasheets/esp32-solo-1_datasheet_en.pdf b/_media/datasheets/esp32-solo-1_datasheet_en.pdf new file mode 100644 index 0000000000..2393570076 Binary files /dev/null and b/_media/datasheets/esp32-solo-1_datasheet_en.pdf differ diff --git a/_media/datasheets/esp32-wroom-32e_esp32-wroom-32ue_datasheet_en.pdf b/_media/datasheets/esp32-wroom-32e_esp32-wroom-32ue_datasheet_en.pdf new file mode 100644 index 0000000000..ba4dff1c5a Binary files /dev/null and b/_media/datasheets/esp32-wroom-32e_esp32-wroom-32ue_datasheet_en.pdf differ diff --git a/_media/datasheets/esp32-wroom-32se_datasheet_en.pdf b/_media/datasheets/esp32-wroom-32se_datasheet_en.pdf new file mode 100644 index 0000000000..56ef9412a7 Binary files /dev/null and b/_media/datasheets/esp32-wroom-32se_datasheet_en.pdf differ diff --git a/_media/datasheets/esp32-wrover-e_esp32-wrover-ie_datasheet_en.pdf b/_media/datasheets/esp32-wrover-e_esp32-wrover-ie_datasheet_en.pdf new file mode 100644 index 0000000000..020d33f255 Binary files /dev/null and b/_media/datasheets/esp32-wrover-e_esp32-wrover-ie_datasheet_en.pdf differ diff --git a/_media/deepsleep_adjust_rules.png b/_media/deepsleep_adjust_rules.png new file mode 100644 index 0000000000..7172610ac2 Binary files /dev/null and b/_media/deepsleep_adjust_rules.png differ diff --git a/_media/deepsleep_deepsleep182.png b/_media/deepsleep_deepsleep182.png new file mode 100644 index 0000000000..aaa5122c84 Binary files /dev/null and b/_media/deepsleep_deepsleep182.png differ diff --git a/_media/deepsleep_gpio16_none.png b/_media/deepsleep_gpio16_none.png new file mode 100644 index 0000000000..a13e12f93d Binary files /dev/null and b/_media/deepsleep_gpio16_none.png differ diff --git a/_media/deepsleep_minimal.png b/_media/deepsleep_minimal.png new file mode 100644 index 0000000000..924ce34b19 Binary files /dev/null and b/_media/deepsleep_minimal.png differ diff --git a/_media/deepsleep_switch.png b/_media/deepsleep_switch.png new file mode 100644 index 0000000000..0cf222e17e Binary files /dev/null and b/_media/deepsleep_switch.png differ diff --git a/_media/deepsleep_transistor.png b/_media/deepsleep_transistor.png new file mode 100644 index 0000000000..72168b7d13 Binary files /dev/null and b/_media/deepsleep_transistor.png differ diff --git a/_media/depsleep_adjust_graph.png b/_media/depsleep_adjust_graph.png new file mode 100644 index 0000000000..dc7d20d34d Binary files /dev/null and b/_media/depsleep_adjust_graph.png differ diff --git a/_media/dves.png b/_media/dves.png new file mode 100644 index 0000000000..7c7c56c5eb Binary files /dev/null and b/_media/dves.png differ diff --git a/_media/emulation_configuration.png b/_media/emulation_configuration.png new file mode 100644 index 0000000000..f15497d216 Binary files /dev/null and b/_media/emulation_configuration.png differ diff --git a/_media/esp32-pio.jpg b/_media/esp32-pio.jpg new file mode 100644 index 0000000000..a7d93f84d9 Binary files /dev/null and b/_media/esp32-pio.jpg differ diff --git a/_media/esp32-uco.jpg b/_media/esp32-uco.jpg new file mode 100644 index 0000000000..57094870df Binary files /dev/null and b/_media/esp32-uco.jpg differ diff --git a/_media/esp8266.png b/_media/esp8266.png new file mode 100644 index 0000000000..0eea496f3d Binary files /dev/null and b/_media/esp8266.png differ diff --git a/_media/favicon.ico b/_media/favicon.ico new file mode 100644 index 0000000000..5f033c8c70 Binary files /dev/null and b/_media/favicon.ico differ diff --git a/_media/frontlogo.svg b/_media/frontlogo.svg new file mode 100644 index 0000000000..a5a7e695b5 --- /dev/null +++ b/_media/frontlogo.svg @@ -0,0 +1,128 @@ + + + + + + image/svg+xml + + Zeichenfläche 1 + + + + + + + + + + + Zeichenfläche 1 + + + + + + + + + + diff --git a/_media/frontlogob.svg b/_media/frontlogob.svg new file mode 100644 index 0000000000..68286f6cb5 --- /dev/null +++ b/_media/frontlogob.svg @@ -0,0 +1,128 @@ + + + + + + image/svg+xml + + Zeichenfläche 1 + + + + + + + + + + + Zeichenfläche 1 + + + + + + + + + + diff --git a/_media/golden-ch340g.png b/_media/golden-ch340g.png new file mode 100644 index 0000000000..709ddb9d2f Binary files /dev/null and b/_media/golden-ch340g.png differ diff --git a/_media/gpio0.png b/_media/gpio0.png new file mode 100644 index 0000000000..e90f08e10a Binary files /dev/null and b/_media/gpio0.png differ diff --git a/_media/hass1.png b/_media/hass1.png new file mode 100644 index 0000000000..b956e32eb4 Binary files /dev/null and b/_media/hass1.png differ diff --git a/_media/hass2.png b/_media/hass2.png new file mode 100644 index 0000000000..f5332eb100 Binary files /dev/null and b/_media/hass2.png differ diff --git a/_media/hass_0.jpg b/_media/hass_0.jpg new file mode 100644 index 0000000000..79af7648e6 Binary files /dev/null and b/_media/hass_0.jpg differ diff --git a/_media/hass_1.jpg b/_media/hass_1.jpg new file mode 100644 index 0000000000..6e7483545b Binary files /dev/null and b/_media/hass_1.jpg differ diff --git a/_media/hass_information_sensor.jpg b/_media/hass_information_sensor.jpg new file mode 100644 index 0000000000..0d62769a18 Binary files /dev/null and b/_media/hass_information_sensor.jpg differ diff --git a/_media/hass_integrations_screen.jpg b/_media/hass_integrations_screen.jpg new file mode 100644 index 0000000000..7e0c2a231d Binary files /dev/null and b/_media/hass_integrations_screen.jpg differ diff --git a/_media/hatasmota_delete.png b/_media/hatasmota_delete.png new file mode 100644 index 0000000000..be42aaffdc Binary files /dev/null and b/_media/hatasmota_delete.png differ diff --git a/_media/hax_pow1.png b/_media/hax_pow1.png new file mode 100644 index 0000000000..b68b2ce0b2 Binary files /dev/null and b/_media/hax_pow1.png differ diff --git a/_media/header.svg b/_media/header.svg new file mode 100644 index 0000000000..af3fc00f21 --- /dev/null +++ b/_media/header.svg @@ -0,0 +1 @@ +Zeichenfläche 1 \ No newline at end of file diff --git a/_media/hm-16_pinout.jpg b/_media/hm-16_pinout.jpg new file mode 100644 index 0000000000..eca5ea2c94 Binary files /dev/null and b/_media/hm-16_pinout.jpg differ diff --git a/_media/hm-17_pinout.jpg b/_media/hm-17_pinout.jpg new file mode 100644 index 0000000000..2c37418ee0 Binary files /dev/null and b/_media/hm-17_pinout.jpg differ diff --git a/_media/hm10_config1.jpg b/_media/hm10_config1.jpg new file mode 100644 index 0000000000..1d82763ae9 Binary files /dev/null and b/_media/hm10_config1.jpg differ diff --git a/_media/hm10_config_success.jpg b/_media/hm10_config_success.jpg new file mode 100644 index 0000000000..06706551cc Binary files /dev/null and b/_media/hm10_config_success.jpg differ diff --git a/_media/ibeacon_success.jpg b/_media/ibeacon_success.jpg new file mode 100644 index 0000000000..11904d8130 Binary files /dev/null and b/_media/ibeacon_success.jpg differ diff --git a/_media/ipv6.png b/_media/ipv6.png new file mode 100644 index 0000000000..956f98c178 Binary files /dev/null and b/_media/ipv6.png differ diff --git a/_media/irremote-sheme.jpg b/_media/irremote-sheme.jpg new file mode 100644 index 0000000000..67274c8fa4 Binary files /dev/null and b/_media/irremote-sheme.jpg differ diff --git a/_media/just-waves.png b/_media/just-waves.png new file mode 100644 index 0000000000..16d7dd2e58 Binary files /dev/null and b/_media/just-waves.png differ diff --git a/_media/light_UI_5channel.png b/_media/light_UI_5channel.png new file mode 100644 index 0000000000..2bc6366ef6 Binary files /dev/null and b/_media/light_UI_5channel.png differ diff --git a/_media/logo-blue.png b/_media/logo-blue.png new file mode 100644 index 0000000000..8b4056ae3d Binary files /dev/null and b/_media/logo-blue.png differ diff --git a/_media/logo-twitter.svg b/_media/logo-twitter.svg new file mode 100644 index 0000000000..0f9fd732c1 --- /dev/null +++ b/_media/logo-twitter.svg @@ -0,0 +1,201 @@ + + + + + + image/svg+xml + + Zeichenfläche 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Zeichenfläche 1 + + + + + + + + + + diff --git a/_media/logo.png b/_media/logo.png new file mode 100644 index 0000000000..4ec6aecbe6 Binary files /dev/null and b/_media/logo.png differ diff --git a/_media/logo.svg b/_media/logo.svg new file mode 100644 index 0000000000..43f86da173 --- /dev/null +++ b/_media/logo.svg @@ -0,0 +1 @@ +Element 1 \ No newline at end of file diff --git a/_media/logo/alexa.png b/_media/logo/alexa.png new file mode 100644 index 0000000000..56162f7260 Binary files /dev/null and b/_media/logo/alexa.png differ diff --git a/_media/logo/domoticz.png b/_media/logo/domoticz.png new file mode 100644 index 0000000000..4350c23c9a Binary files /dev/null and b/_media/logo/domoticz.png differ diff --git a/_media/logo/domoticz2.jpg b/_media/logo/domoticz2.jpg new file mode 100644 index 0000000000..07962fac10 Binary files /dev/null and b/_media/logo/domoticz2.jpg differ diff --git a/_media/logo/domoticz3.jpg b/_media/logo/domoticz3.jpg new file mode 100644 index 0000000000..96d480afc5 Binary files /dev/null and b/_media/logo/domoticz3.jpg differ diff --git a/_media/logo/gladys.png b/_media/logo/gladys.png new file mode 100644 index 0000000000..7e663cf4b8 Binary files /dev/null and b/_media/logo/gladys.png differ diff --git a/_media/logo/home-assistant.png b/_media/logo/home-assistant.png new file mode 100644 index 0000000000..24fdd1628c Binary files /dev/null and b/_media/logo/home-assistant.png differ diff --git a/_media/logo/iobroker.png b/_media/logo/iobroker.png new file mode 100644 index 0000000000..2ae4aaf056 Binary files /dev/null and b/_media/logo/iobroker.png differ diff --git a/_media/logo/knx.png b/_media/logo/knx.png new file mode 100644 index 0000000000..7f5e9d47c5 Binary files /dev/null and b/_media/logo/knx.png differ diff --git a/_media/logo/openhab.png b/_media/logo/openhab.png new file mode 100644 index 0000000000..3783a10a0a Binary files /dev/null and b/_media/logo/openhab.png differ diff --git a/_media/logob.svg b/_media/logob.svg new file mode 100644 index 0000000000..3816087961 --- /dev/null +++ b/_media/logob.svg @@ -0,0 +1,255 @@ + + + + + + image/svg+xml + + Tasmota Logo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tasmota Logo + + + + + + + + + + + diff --git a/_media/logog.pdn b/_media/logog.pdn new file mode 100644 index 0000000000..91dd880eae Binary files /dev/null and b/_media/logog.pdn differ diff --git a/_media/logog.png b/_media/logog.png new file mode 100644 index 0000000000..b1ff15f7ab Binary files /dev/null and b/_media/logog.png differ diff --git a/_media/logog_mini.png b/_media/logog_mini.png new file mode 100644 index 0000000000..b225ab3067 Binary files /dev/null and b/_media/logog_mini.png differ diff --git a/_media/logow.png b/_media/logow.png new file mode 100644 index 0000000000..fbf7001a8f Binary files /dev/null and b/_media/logow.png differ diff --git a/_media/lolin_sht30_shield_v2.0.1.jpg b/_media/lolin_sht30_shield_v2.0.1.jpg new file mode 100644 index 0000000000..803aaf9b6f Binary files /dev/null and b/_media/lolin_sht30_shield_v2.0.1.jpg differ diff --git a/_media/lvgl/Flash_complete.png b/_media/lvgl/Flash_complete.png new file mode 100644 index 0000000000..4abf75ed39 Binary files /dev/null and b/_media/lvgl/Flash_complete.png differ diff --git a/_media/lvgl/Flash_confirm.png b/_media/lvgl/Flash_confirm.png new file mode 100644 index 0000000000..22ad771c13 Binary files /dev/null and b/_media/lvgl/Flash_confirm.png differ diff --git a/_media/lvgl/Flash_erase.png b/_media/lvgl/Flash_erase.png new file mode 100644 index 0000000000..a15062c68f Binary files /dev/null and b/_media/lvgl/Flash_erase.png differ diff --git a/_media/lvgl/Flash_install.png b/_media/lvgl/Flash_install.png new file mode 100644 index 0000000000..7a5ebc6c98 Binary files /dev/null and b/_media/lvgl/Flash_install.png differ diff --git a/_media/lvgl/Flash_main-screen.png b/_media/lvgl/Flash_main-screen.png new file mode 100644 index 0000000000..425cc410ca Binary files /dev/null and b/_media/lvgl/Flash_main-screen.png differ diff --git a/_media/lvgl/Flash_select_firmware.png b/_media/lvgl/Flash_select_firmware.png new file mode 100644 index 0000000000..2cf85fa577 Binary files /dev/null and b/_media/lvgl/Flash_select_firmware.png differ diff --git a/_media/lvgl/Flash_serial_port.png b/_media/lvgl/Flash_serial_port.png new file mode 100644 index 0000000000..e2449a2945 Binary files /dev/null and b/_media/lvgl/Flash_serial_port.png differ diff --git a/_media/lvgl/M5Stack.png b/_media/lvgl/M5Stack.png new file mode 100644 index 0000000000..5d5f942317 Binary files /dev/null and b/_media/lvgl/M5Stack.png differ diff --git a/_media/lvgl/Tasmota_autoconf.png b/_media/lvgl/Tasmota_autoconf.png new file mode 100644 index 0000000000..550200d808 Binary files /dev/null and b/_media/lvgl/Tasmota_autoconf.png differ diff --git a/_media/lvgl/Tasmota_config.png b/_media/lvgl/Tasmota_config.png new file mode 100644 index 0000000000..2ec884112e Binary files /dev/null and b/_media/lvgl/Tasmota_config.png differ diff --git a/_media/lvgl/Tasmota_fs.png b/_media/lvgl/Tasmota_fs.png new file mode 100644 index 0000000000..48a89b1b3f Binary files /dev/null and b/_media/lvgl/Tasmota_fs.png differ diff --git a/_media/lvgl/Tasmota_ip.png b/_media/lvgl/Tasmota_ip.png new file mode 100644 index 0000000000..5533c867d4 Binary files /dev/null and b/_media/lvgl/Tasmota_ip.png differ diff --git a/_media/lvgl/Tasmota_lvgldemo.png b/_media/lvgl/Tasmota_lvgldemo.png new file mode 100644 index 0000000000..d2867068ae Binary files /dev/null and b/_media/lvgl/Tasmota_lvgldemo.png differ diff --git a/_media/lvgl/Tasmota_main.png b/_media/lvgl/Tasmota_main.png new file mode 100644 index 0000000000..f8a15c9a97 Binary files /dev/null and b/_media/lvgl/Tasmota_main.png differ diff --git a/_media/lvgl/Tasmota_main2.png b/_media/lvgl/Tasmota_main2.png new file mode 100644 index 0000000000..18a347f22e Binary files /dev/null and b/_media/lvgl/Tasmota_main2.png differ diff --git a/_media/lvgl/Tasmota_wifi_ap.png b/_media/lvgl/Tasmota_wifi_ap.png new file mode 100644 index 0000000000..d227edf5a0 Binary files /dev/null and b/_media/lvgl/Tasmota_wifi_ap.png differ diff --git a/_media/lvgl/Tasmota_wifi_select.png b/_media/lvgl/Tasmota_wifi_select.png new file mode 100644 index 0000000000..5e9e425a72 Binary files /dev/null and b/_media/lvgl/Tasmota_wifi_select.png differ diff --git a/_media/lvgl/lvgl_demo.png b/_media/lvgl/lvgl_demo.png new file mode 100644 index 0000000000..d2393eb355 Binary files /dev/null and b/_media/lvgl/lvgl_demo.png differ diff --git a/_media/matter/add_remote.jpg b/_media/matter/add_remote.jpg new file mode 100644 index 0000000000..e030ed0c75 Binary files /dev/null and b/_media/matter/add_remote.jpg differ diff --git a/_media/matter/add_remote_completed.jpg b/_media/matter/add_remote_completed.jpg new file mode 100644 index 0000000000..873a33a0b4 Binary files /dev/null and b/_media/matter/add_remote_completed.jpg differ diff --git a/_media/matter/add_remote_submenu.jpg b/_media/matter/add_remote_submenu.jpg new file mode 100644 index 0000000000..89c605555b Binary files /dev/null and b/_media/matter/add_remote_submenu.jpg differ diff --git a/_media/matter/add_to_configuration.jpg b/_media/matter/add_to_configuration.jpg new file mode 100644 index 0000000000..f814049494 Binary files /dev/null and b/_media/matter/add_to_configuration.jpg differ diff --git a/_media/matter/commissioning.jpg b/_media/matter/commissioning.jpg new file mode 100644 index 0000000000..4c7219e17f Binary files /dev/null and b/_media/matter/commissioning.jpg differ diff --git a/_media/matter/current_configuration.jpg b/_media/matter/current_configuration.jpg new file mode 100644 index 0000000000..23ddb28827 Binary files /dev/null and b/_media/matter/current_configuration.jpg differ diff --git a/_media/matter/enable.jpg b/_media/matter/enable.jpg new file mode 100644 index 0000000000..e59e109280 Binary files /dev/null and b/_media/matter/enable.jpg differ diff --git a/_media/matter/endpoint_config.jpg b/_media/matter/endpoint_config.jpg new file mode 100644 index 0000000000..6b91d8954b Binary files /dev/null and b/_media/matter/endpoint_config.jpg differ diff --git a/_media/matter/endpoint_configured.jpg b/_media/matter/endpoint_configured.jpg new file mode 100644 index 0000000000..0ddc608919 Binary files /dev/null and b/_media/matter/endpoint_configured.jpg differ diff --git a/_media/matter/fabrics.jpg b/_media/matter/fabrics.jpg new file mode 100644 index 0000000000..2bbe2b76ba Binary files /dev/null and b/_media/matter/fabrics.jpg differ diff --git a/_media/matter/google/step1.jpg b/_media/matter/google/step1.jpg new file mode 100644 index 0000000000..95f3692525 Binary files /dev/null and b/_media/matter/google/step1.jpg differ diff --git a/_media/matter/google/step2.jpg b/_media/matter/google/step2.jpg new file mode 100644 index 0000000000..27db2b2825 Binary files /dev/null and b/_media/matter/google/step2.jpg differ diff --git a/_media/matter/google/step3.jpg b/_media/matter/google/step3.jpg new file mode 100644 index 0000000000..182508407c Binary files /dev/null and b/_media/matter/google/step3.jpg differ diff --git a/_media/matter/google/step4.jpg b/_media/matter/google/step4.jpg new file mode 100644 index 0000000000..bfd38f40d6 Binary files /dev/null and b/_media/matter/google/step4.jpg differ diff --git a/_media/matter/google/step5.jpg b/_media/matter/google/step5.jpg new file mode 100644 index 0000000000..bdbfb52213 Binary files /dev/null and b/_media/matter/google/step5.jpg differ diff --git a/_media/matter/google/step6.jpg b/_media/matter/google/step6.jpg new file mode 100644 index 0000000000..4dcedbbf8f Binary files /dev/null and b/_media/matter/google/step6.jpg differ diff --git a/_media/matter/google/step7.jpg b/_media/matter/google/step7.jpg new file mode 100644 index 0000000000..8b95e825a1 Binary files /dev/null and b/_media/matter/google/step7.jpg differ diff --git a/_media/matter/matter_configuration.jpg b/_media/matter/matter_configuration.jpg new file mode 100644 index 0000000000..35440111b4 Binary files /dev/null and b/_media/matter/matter_configuration.jpg differ diff --git a/_media/matter/passcode.jpg b/_media/matter/passcode.jpg new file mode 100644 index 0000000000..971f07d6dc Binary files /dev/null and b/_media/matter/passcode.jpg differ diff --git a/_media/meter3fpro380-mbx-5a.png b/_media/meter3fpro380-mbx-5a.png new file mode 100644 index 0000000000..f145349b05 Binary files /dev/null and b/_media/meter3fpro380-mbx-5a.png differ diff --git a/_media/ml/audio_file.jpg b/_media/ml/audio_file.jpg new file mode 100644 index 0000000000..01bc05f5ef Binary files /dev/null and b/_media/ml/audio_file.jpg differ diff --git a/_media/ml/download.png b/_media/ml/download.png new file mode 100644 index 0000000000..1180ab63c0 Binary files /dev/null and b/_media/ml/download.png differ diff --git a/_media/ml/feature_parameters.png b/_media/ml/feature_parameters.png new file mode 100644 index 0000000000..d6b3938d1b Binary files /dev/null and b/_media/ml/feature_parameters.png differ diff --git a/_media/ml/features.png b/_media/ml/features.png new file mode 100644 index 0000000000..4b445be19a Binary files /dev/null and b/_media/ml/features.png differ diff --git a/_media/ml/nn_arch.png b/_media/ml/nn_arch.png new file mode 100644 index 0000000000..53e36b9b79 Binary files /dev/null and b/_media/ml/nn_arch.png differ diff --git a/_media/opentherm/configure.png b/_media/opentherm/configure.png new file mode 100644 index 0000000000..0ca15861a6 Binary files /dev/null and b/_media/opentherm/configure.png differ diff --git a/_media/opentherm/hw.jpeg b/_media/opentherm/hw.jpeg new file mode 100644 index 0000000000..30de0484a5 Binary files /dev/null and b/_media/opentherm/hw.jpeg differ diff --git a/_media/opentherm/logging.png b/_media/opentherm/logging.png new file mode 100644 index 0000000000..e6ae8e4e5e Binary files /dev/null and b/_media/opentherm/logging.png differ diff --git a/_media/opentherm/opentherm_gw.png b/_media/opentherm/opentherm_gw.png new file mode 100644 index 0000000000..65884ee285 Binary files /dev/null and b/_media/opentherm/opentherm_gw.png differ diff --git a/_media/opentherm/opentherm_master.png b/_media/opentherm/opentherm_master.png new file mode 100644 index 0000000000..0c3b2bef6f Binary files /dev/null and b/_media/opentherm/opentherm_master.png differ diff --git a/_media/opentherm/thermostat.jpg b/_media/opentherm/thermostat.jpg new file mode 100644 index 0000000000..7773dcb6d8 Binary files /dev/null and b/_media/opentherm/thermostat.jpg differ diff --git a/_media/p1-smartmeter/p1-smartmeter.png b/_media/p1-smartmeter/p1-smartmeter.png new file mode 100644 index 0000000000..8982179cb8 Binary files /dev/null and b/_media/p1-smartmeter/p1-smartmeter.png differ diff --git a/_media/p1-smartmeter/p1-smartmeter_v2.png b/_media/p1-smartmeter/p1-smartmeter_v2.png new file mode 100644 index 0000000000..0f3f172779 Binary files /dev/null and b/_media/p1-smartmeter/p1-smartmeter_v2.png differ diff --git a/_media/pcf8574_configure.png b/_media/pcf8574_configure.png new file mode 100644 index 0000000000..47dc42ca48 Binary files /dev/null and b/_media/pcf8574_configure.png differ diff --git a/_media/pcf8574_inputgui.png b/_media/pcf8574_inputgui.png new file mode 100644 index 0000000000..fb9cd12772 Binary files /dev/null and b/_media/pcf8574_inputgui.png differ diff --git a/_media/pcf8574_module.png b/_media/pcf8574_module.png new file mode 100644 index 0000000000..1cfed9389d Binary files /dev/null and b/_media/pcf8574_module.png differ diff --git a/_media/pcf8574_powergui.png b/_media/pcf8574_powergui.png new file mode 100644 index 0000000000..f493095c9d Binary files /dev/null and b/_media/pcf8574_powergui.png differ diff --git a/_media/peripherals/AHT10.jpg b/_media/peripherals/AHT10.jpg new file mode 100644 index 0000000000..92d2a6184c Binary files /dev/null and b/_media/peripherals/AHT10.jpg differ diff --git a/_media/peripherals/AHT15.jpg b/_media/peripherals/AHT15.jpg new file mode 100644 index 0000000000..77f1379fdb Binary files /dev/null and b/_media/peripherals/AHT15.jpg differ diff --git a/_media/peripherals/AHT20.jpg b/_media/peripherals/AHT20.jpg new file mode 100644 index 0000000000..df99dc2068 Binary files /dev/null and b/_media/peripherals/AHT20.jpg differ diff --git a/_media/peripherals/AHT21.jpg b/_media/peripherals/AHT21.jpg new file mode 100644 index 0000000000..be4f5d3c5c Binary files /dev/null and b/_media/peripherals/AHT21.jpg differ diff --git a/_media/peripherals/AHT25.jpg b/_media/peripherals/AHT25.jpg new file mode 100644 index 0000000000..2cfb6000dd Binary files /dev/null and b/_media/peripherals/AHT25.jpg differ diff --git a/_media/peripherals/AM2301B.jpg b/_media/peripherals/AM2301B.jpg new file mode 100644 index 0000000000..ee9406aa05 Binary files /dev/null and b/_media/peripherals/AM2301B.jpg differ diff --git a/_media/peripherals/AS3935_webui.jpg b/_media/peripherals/AS3935_webui.jpg new file mode 100644 index 0000000000..bb03cf2023 Binary files /dev/null and b/_media/peripherals/AS3935_webui.jpg differ diff --git a/_media/peripherals/CGD1.png b/_media/peripherals/CGD1.png new file mode 100644 index 0000000000..ed2a046de6 Binary files /dev/null and b/_media/peripherals/CGD1.png differ diff --git a/_media/peripherals/CGG1.png b/_media/peripherals/CGG1.png new file mode 100644 index 0000000000..40110b6aa0 Binary files /dev/null and b/_media/peripherals/CGG1.png differ diff --git a/_media/peripherals/CJMCU_AS3935.jpg b/_media/peripherals/CJMCU_AS3935.jpg new file mode 100644 index 0000000000..55d5d28287 Binary files /dev/null and b/_media/peripherals/CJMCU_AS3935.jpg differ diff --git a/_media/peripherals/CJMCU_AS3935_Wrong.jpg b/_media/peripherals/CJMCU_AS3935_Wrong.jpg new file mode 100644 index 0000000000..8b630e7739 Binary files /dev/null and b/_media/peripherals/CJMCU_AS3935_Wrong.jpg differ diff --git a/_media/peripherals/DY-SV17F_pinout.jpg b/_media/peripherals/DY-SV17F_pinout.jpg new file mode 100644 index 0000000000..c0025a8cb2 Binary files /dev/null and b/_media/peripherals/DY-SV17F_pinout.jpg differ diff --git a/_media/peripherals/EA_MOD1016.jpg b/_media/peripherals/EA_MOD1016.jpg new file mode 100644 index 0000000000..fb1f6d4fbb Binary files /dev/null and b/_media/peripherals/EA_MOD1016.jpg differ diff --git a/_media/peripherals/GY_AS3935.jpg b/_media/peripherals/GY_AS3935.jpg new file mode 100644 index 0000000000..974643dbf2 Binary files /dev/null and b/_media/peripherals/GY_AS3935.jpg differ diff --git a/_media/peripherals/MAX7219-front.png b/_media/peripherals/MAX7219-front.png new file mode 100644 index 0000000000..03778038a0 Binary files /dev/null and b/_media/peripherals/MAX7219-front.png differ diff --git a/_media/peripherals/MCP9808.jpg b/_media/peripherals/MCP9808.jpg new file mode 100644 index 0000000000..69644e1c59 Binary files /dev/null and b/_media/peripherals/MCP9808.jpg differ diff --git a/_media/peripherals/MFRC522.jpg b/_media/peripherals/MFRC522.jpg new file mode 100644 index 0000000000..3b2564e2ff Binary files /dev/null and b/_media/peripherals/MFRC522.jpg differ diff --git a/_media/peripherals/MFRC522_webui.jpg b/_media/peripherals/MFRC522_webui.jpg new file mode 100644 index 0000000000..4db88f56aa Binary files /dev/null and b/_media/peripherals/MFRC522_webui.jpg differ diff --git a/_media/peripherals/MH-SR602-pinout.png b/_media/peripherals/MH-SR602-pinout.png new file mode 100644 index 0000000000..80ca8a8a87 Binary files /dev/null and b/_media/peripherals/MH-SR602-pinout.png differ diff --git a/_media/peripherals/MH-SR602-table.png b/_media/peripherals/MH-SR602-table.png new file mode 100644 index 0000000000..4707bd1e2c Binary files /dev/null and b/_media/peripherals/MH-SR602-table.png differ diff --git a/_media/peripherals/MH-SR602.jpg b/_media/peripherals/MH-SR602.jpg new file mode 100644 index 0000000000..9357e1cb08 Binary files /dev/null and b/_media/peripherals/MH-SR602.jpg differ diff --git a/_media/peripherals/MPU6050.jpg b/_media/peripherals/MPU6050.jpg new file mode 100644 index 0000000000..e07add9eed Binary files /dev/null and b/_media/peripherals/MPU6050.jpg differ diff --git a/_media/peripherals/PWF_AS3935.jpg b/_media/peripherals/PWF_AS3935.jpg new file mode 100644 index 0000000000..0a738b10bd Binary files /dev/null and b/_media/peripherals/PWF_AS3935.jpg differ diff --git a/_media/peripherals/Robotdyn-Dimmermodul-1Kanal.jpg b/_media/peripherals/Robotdyn-Dimmermodul-1Kanal.jpg new file mode 100644 index 0000000000..d59805e170 Binary files /dev/null and b/_media/peripherals/Robotdyn-Dimmermodul-1Kanal.jpg differ diff --git a/_media/peripherals/SRX882.jpg b/_media/peripherals/SRX882.jpg new file mode 100644 index 0000000000..8e2d38dbbd Binary files /dev/null and b/_media/peripherals/SRX882.jpg differ diff --git a/_media/peripherals/STX882.jpg b/_media/peripherals/STX882.jpg new file mode 100644 index 0000000000..d2b329baeb Binary files /dev/null and b/_media/peripherals/STX882.jpg differ diff --git a/_media/peripherals/Sparkfun_AS3935.jpg b/_media/peripherals/Sparkfun_AS3935.jpg new file mode 100644 index 0000000000..47bcec5f1b Binary files /dev/null and b/_media/peripherals/Sparkfun_AS3935.jpg differ diff --git a/_media/peripherals/TM1637-012.jpg b/_media/peripherals/TM1637-012.jpg new file mode 100644 index 0000000000..00dcb1736d Binary files /dev/null and b/_media/peripherals/TM1637-012.jpg differ diff --git a/_media/peripherals/TM1637-2.5.jpg b/_media/peripherals/TM1637-2.5.jpg new file mode 100644 index 0000000000..fd34431a32 Binary files /dev/null and b/_media/peripherals/TM1637-2.5.jpg differ diff --git a/_media/peripherals/TM1637-22.5d.jpg b/_media/peripherals/TM1637-22.5d.jpg new file mode 100644 index 0000000000..f0a8b8feba Binary files /dev/null and b/_media/peripherals/TM1637-22.5d.jpg differ diff --git a/_media/peripherals/TM1637-8.8.jpg b/_media/peripherals/TM1637-8.8.jpg new file mode 100644 index 0000000000..a05c61df63 Binary files /dev/null and b/_media/peripherals/TM1637-8.8.jpg differ diff --git a/_media/peripherals/TM1637-Wemos-D1-Mini.png b/_media/peripherals/TM1637-Wemos-D1-Mini.png new file mode 100644 index 0000000000..9d0ba7b08b Binary files /dev/null and b/_media/peripherals/TM1637-Wemos-D1-Mini.png differ diff --git a/_media/peripherals/TM1637-back-and-front.png b/_media/peripherals/TM1637-back-and-front.png new file mode 100644 index 0000000000..c49a96fdb7 Binary files /dev/null and b/_media/peripherals/TM1637-back-and-front.png differ diff --git a/_media/peripherals/TM1637-front.png b/_media/peripherals/TM1637-front.png new file mode 100644 index 0000000000..8ab6419cfa Binary files /dev/null and b/_media/peripherals/TM1637-front.png differ diff --git a/_media/peripherals/TM1637-level.jpg b/_media/peripherals/TM1637-level.jpg new file mode 100644 index 0000000000..807a8457df Binary files /dev/null and b/_media/peripherals/TM1637-level.jpg differ diff --git a/_media/peripherals/TM1638-front.png b/_media/peripherals/TM1638-front.png new file mode 100644 index 0000000000..39155174d4 Binary files /dev/null and b/_media/peripherals/TM1638-front.png differ diff --git a/_media/peripherals/TM1650-303wifilc01.jpg b/_media/peripherals/TM1650-303wifilc01.jpg new file mode 100644 index 0000000000..99168206fd Binary files /dev/null and b/_media/peripherals/TM1650-303wifilc01.jpg differ diff --git a/_media/peripherals/TM1650-XY-Clock.jpg b/_media/peripherals/TM1650-XY-Clock.jpg new file mode 100644 index 0000000000..2afaa98505 Binary files /dev/null and b/_media/peripherals/TM1650-XY-Clock.jpg differ diff --git a/_media/peripherals/VEML6075_breakout.jpg b/_media/peripherals/VEML6075_breakout.jpg new file mode 100644 index 0000000000..6efb93039f Binary files /dev/null and b/_media/peripherals/VEML6075_breakout.jpg differ diff --git a/_media/peripherals/VEML7700_breakout.jpg b/_media/peripherals/VEML7700_breakout.jpg new file mode 100644 index 0000000000..b51b187809 Binary files /dev/null and b/_media/peripherals/VEML7700_breakout.jpg differ diff --git a/_media/peripherals/ZCDimmer_Config.png b/_media/peripherals/ZCDimmer_Config.png new file mode 100644 index 0000000000..a5d4bab164 Binary files /dev/null and b/_media/peripherals/ZCDimmer_Config.png differ diff --git a/_media/peripherals/ZCDimmer_Schematic.png b/_media/peripherals/ZCDimmer_Schematic.png new file mode 100644 index 0000000000..ffa4b94e19 Binary files /dev/null and b/_media/peripherals/ZCDimmer_Schematic.png differ diff --git a/_media/peripherals/aht1x-readout.jpg b/_media/peripherals/aht1x-readout.jpg new file mode 100644 index 0000000000..436242b0cd Binary files /dev/null and b/_media/peripherals/aht1x-readout.jpg differ diff --git a/_media/peripherals/dfplayer_pinout.jpg b/_media/peripherals/dfplayer_pinout.jpg new file mode 100644 index 0000000000..fa9742efa6 Binary files /dev/null and b/_media/peripherals/dfplayer_pinout.jpg differ diff --git a/_media/peripherals/dfplayer_wiring.jpg b/_media/peripherals/dfplayer_wiring.jpg new file mode 100644 index 0000000000..81a0203fc7 Binary files /dev/null and b/_media/peripherals/dfplayer_wiring.jpg differ diff --git a/_media/peripherals/ezo.png b/_media/peripherals/ezo.png new file mode 100644 index 0000000000..95445c2ac0 Binary files /dev/null and b/_media/peripherals/ezo.png differ diff --git a/_media/peripherals/hm-10-1.jpg b/_media/peripherals/hm-10-1.jpg new file mode 100644 index 0000000000..babae1ab60 Binary files /dev/null and b/_media/peripherals/hm-10-1.jpg differ diff --git a/_media/peripherals/hm-10.jpg b/_media/peripherals/hm-10.jpg new file mode 100644 index 0000000000..c334e0dc62 Binary files /dev/null and b/_media/peripherals/hm-10.jpg differ diff --git a/_media/peripherals/hm-16.jpg b/_media/peripherals/hm-16.jpg new file mode 100644 index 0000000000..63afe81897 Binary files /dev/null and b/_media/peripherals/hm-16.jpg differ diff --git a/_media/peripherals/hm-17.jpg b/_media/peripherals/hm-17.jpg new file mode 100644 index 0000000000..870bc25d20 Binary files /dev/null and b/_media/peripherals/hm-17.jpg differ diff --git a/_media/peripherals/i2s_dac.png b/_media/peripherals/i2s_dac.png new file mode 100644 index 0000000000..744e890445 Binary files /dev/null and b/_media/peripherals/i2s_dac.png differ diff --git a/_media/peripherals/i2s_microphone.png b/_media/peripherals/i2s_microphone.png new file mode 100644 index 0000000000..eefb6d58a2 Binary files /dev/null and b/_media/peripherals/i2s_microphone.png differ diff --git a/_media/peripherals/iaq-breakout.jpg b/_media/peripherals/iaq-breakout.jpg new file mode 100644 index 0000000000..b4bde62b3a Binary files /dev/null and b/_media/peripherals/iaq-breakout.jpg differ diff --git a/_media/peripherals/iaq-pinout.jpg b/_media/peripherals/iaq-pinout.jpg new file mode 100644 index 0000000000..f563b7bfac Binary files /dev/null and b/_media/peripherals/iaq-pinout.jpg differ diff --git a/_media/peripherals/iaq-readout.jpg b/_media/peripherals/iaq-readout.jpg new file mode 100644 index 0000000000..b48268b32d Binary files /dev/null and b/_media/peripherals/iaq-readout.jpg differ diff --git a/_media/peripherals/iaq-sensor.jpg b/_media/peripherals/iaq-sensor.jpg new file mode 100644 index 0000000000..1d727e77f5 Binary files /dev/null and b/_media/peripherals/iaq-sensor.jpg differ diff --git a/_media/peripherals/iaq-warmup.jpg b/_media/peripherals/iaq-warmup.jpg new file mode 100644 index 0000000000..baf3575f7b Binary files /dev/null and b/_media/peripherals/iaq-warmup.jpg differ diff --git a/_media/peripherals/lywsd02.jpg b/_media/peripherals/lywsd02.jpg new file mode 100644 index 0000000000..a26dffd19b Binary files /dev/null and b/_media/peripherals/lywsd02.jpg differ diff --git a/_media/peripherals/lywsd03.png b/_media/peripherals/lywsd03.png new file mode 100644 index 0000000000..1d84d8d154 Binary files /dev/null and b/_media/peripherals/lywsd03.png differ diff --git a/_media/peripherals/mcu-6050-webui.png b/_media/peripherals/mcu-6050-webui.png new file mode 100644 index 0000000000..1d8b5241f8 Binary files /dev/null and b/_media/peripherals/mcu-6050-webui.png differ diff --git a/_media/peripherals/mcu-6050-webui2.png b/_media/peripherals/mcu-6050-webui2.png new file mode 100644 index 0000000000..6a86524b3c Binary files /dev/null and b/_media/peripherals/mcu-6050-webui2.png differ diff --git a/_media/peripherals/miflora.png b/_media/peripherals/miflora.png new file mode 100644 index 0000000000..d130d2de41 Binary files /dev/null and b/_media/peripherals/miflora.png differ diff --git a/_media/peripherals/mlx90614-1.jpg b/_media/peripherals/mlx90614-1.jpg new file mode 100644 index 0000000000..239f76ab29 Binary files /dev/null and b/_media/peripherals/mlx90614-1.jpg differ diff --git a/_media/peripherals/mlx90614-2.jpg b/_media/peripherals/mlx90614-2.jpg new file mode 100644 index 0000000000..bf224edeb7 Binary files /dev/null and b/_media/peripherals/mlx90614-2.jpg differ diff --git a/_media/peripherals/mlx90614-3.jpg b/_media/peripherals/mlx90614-3.jpg new file mode 100644 index 0000000000..5b3cacf29e Binary files /dev/null and b/_media/peripherals/mlx90614-3.jpg differ diff --git a/_media/peripherals/mlx90614-4.jpg b/_media/peripherals/mlx90614-4.jpg new file mode 100644 index 0000000000..a592da932e Binary files /dev/null and b/_media/peripherals/mlx90614-4.jpg differ diff --git a/_media/peripherals/mlx90614-webui.jpg b/_media/peripherals/mlx90614-webui.jpg new file mode 100644 index 0000000000..6dd6ae5cd0 Binary files /dev/null and b/_media/peripherals/mlx90614-webui.jpg differ diff --git a/_media/peripherals/mlx90615-1.jpg b/_media/peripherals/mlx90615-1.jpg new file mode 100644 index 0000000000..c59009c8f6 Binary files /dev/null and b/_media/peripherals/mlx90615-1.jpg differ diff --git a/_media/peripherals/mlx90640.jpg b/_media/peripherals/mlx90640.jpg new file mode 100644 index 0000000000..3412e2b1e5 Binary files /dev/null and b/_media/peripherals/mlx90640.jpg differ diff --git a/_media/peripherals/mlx90640_web.png b/_media/peripherals/mlx90640_web.png new file mode 100644 index 0000000000..8cc6bc9fc2 Binary files /dev/null and b/_media/peripherals/mlx90640_web.png differ diff --git a/_media/peripherals/nrf24_config.png b/_media/peripherals/nrf24_config.png new file mode 100644 index 0000000000..9576ab10ad Binary files /dev/null and b/_media/peripherals/nrf24_config.png differ diff --git a/_media/peripherals/stepper-motor1.png b/_media/peripherals/stepper-motor1.png new file mode 100644 index 0000000000..123bbbc5ee Binary files /dev/null and b/_media/peripherals/stepper-motor1.png differ diff --git a/_media/peripherals/stepper-motor2.png b/_media/peripherals/stepper-motor2.png new file mode 100644 index 0000000000..0b1972692b Binary files /dev/null and b/_media/peripherals/stepper-motor2.png differ diff --git a/_media/peripherals/stepper-motor3.png b/_media/peripherals/stepper-motor3.png new file mode 100644 index 0000000000..ab9c6f07c4 Binary files /dev/null and b/_media/peripherals/stepper-motor3.png differ diff --git a/_media/peripherals/tx23.png b/_media/peripherals/tx23.png new file mode 100644 index 0000000000..f9aaca94ef Binary files /dev/null and b/_media/peripherals/tx23.png differ diff --git a/_media/peripherals/vl53l0x-1.jpg b/_media/peripherals/vl53l0x-1.jpg new file mode 100644 index 0000000000..fa7d7da4ae Binary files /dev/null and b/_media/peripherals/vl53l0x-1.jpg differ diff --git a/_media/peripherals/vl53l0x-2.jpg b/_media/peripherals/vl53l0x-2.jpg new file mode 100644 index 0000000000..c86aa8a23e Binary files /dev/null and b/_media/peripherals/vl53l0x-2.jpg differ diff --git a/_media/peripherals/vl53l0x.png b/_media/peripherals/vl53l0x.png new file mode 100644 index 0000000000..07d41f0fd5 Binary files /dev/null and b/_media/peripherals/vl53l0x.png differ diff --git a/_media/pinheaders.png b/_media/pinheaders.png new file mode 100644 index 0000000000..c522c48dde Binary files /dev/null and b/_media/pinheaders.png differ diff --git a/_media/pinouts/449A-ECOPLUGS_pinout.jpg b/_media/pinouts/449A-ECOPLUGS_pinout.jpg new file mode 100644 index 0000000000..b0d6fc3c20 Binary files /dev/null and b/_media/pinouts/449A-ECOPLUGS_pinout.jpg differ diff --git a/_media/pinouts/ECOPLUGS-449A_pinout.jpg b/_media/pinouts/ECOPLUGS-449A_pinout.jpg new file mode 100644 index 0000000000..d286859e6d Binary files /dev/null and b/_media/pinouts/ECOPLUGS-449A_pinout.jpg differ diff --git a/_media/pinouts/ESP-12K.png b/_media/pinouts/ESP-12K.png new file mode 100644 index 0000000000..20eb12007d Binary files /dev/null and b/_media/pinouts/ESP-12K.png differ diff --git a/_media/pinouts/ESP-12_pinout.jpg b/_media/pinouts/ESP-12_pinout.jpg new file mode 100644 index 0000000000..df2334b5e4 Binary files /dev/null and b/_media/pinouts/ESP-12_pinout.jpg differ diff --git a/_media/pinouts/ESP-12s_pinout.jpg b/_media/pinouts/ESP-12s_pinout.jpg new file mode 100644 index 0000000000..a8f3d5cbcc Binary files /dev/null and b/_media/pinouts/ESP-12s_pinout.jpg differ diff --git a/_media/pinouts/ESP-8266-S3_pinout.jpg b/_media/pinouts/ESP-8266-S3_pinout.jpg new file mode 100644 index 0000000000..e936523d7f Binary files /dev/null and b/_media/pinouts/ESP-8266-S3_pinout.jpg differ diff --git a/_media/pinouts/ESP-C3-01M.png b/_media/pinouts/ESP-C3-01M.png new file mode 100644 index 0000000000..118467bcad Binary files /dev/null and b/_media/pinouts/ESP-C3-01M.png differ diff --git a/_media/pinouts/ESP-C3-12F.png b/_media/pinouts/ESP-C3-12F.png new file mode 100644 index 0000000000..d7ec50d4b2 Binary files /dev/null and b/_media/pinouts/ESP-C3-12F.png differ diff --git a/_media/pinouts/ESP-C3-13.png b/_media/pinouts/ESP-C3-13.png new file mode 100644 index 0000000000..c627a852d9 Binary files /dev/null and b/_media/pinouts/ESP-C3-13.png differ diff --git a/_media/pinouts/ESP-C3-32S.png b/_media/pinouts/ESP-C3-32S.png new file mode 100644 index 0000000000..cd17b3e8a5 Binary files /dev/null and b/_media/pinouts/ESP-C3-32S.png differ diff --git a/_media/pinouts/ESP-WROOM-02_pinout.jpg b/_media/pinouts/ESP-WROOM-02_pinout.jpg new file mode 100644 index 0000000000..849ca62029 Binary files /dev/null and b/_media/pinouts/ESP-WROOM-02_pinout.jpg differ diff --git a/_media/pinouts/ESP-WROOM-32_pinout.jpg b/_media/pinouts/ESP-WROOM-32_pinout.jpg new file mode 100644 index 0000000000..76d824455a Binary files /dev/null and b/_media/pinouts/ESP-WROOM-32_pinout.jpg differ diff --git a/_media/pinouts/ESP32-CAM_pinout.jpg b/_media/pinouts/ESP32-CAM_pinout.jpg new file mode 100644 index 0000000000..0edffe77c2 Binary files /dev/null and b/_media/pinouts/ESP32-CAM_pinout.jpg differ diff --git a/_media/pinouts/ESP32-SOLO-1.png b/_media/pinouts/ESP32-SOLO-1.png new file mode 100644 index 0000000000..0c4dcbaecd Binary files /dev/null and b/_media/pinouts/ESP32-SOLO-1.png differ diff --git a/_media/pinouts/ESP32-SOLO-1_pinout.jpg b/_media/pinouts/ESP32-SOLO-1_pinout.jpg new file mode 100644 index 0000000000..bdfca4e9db Binary files /dev/null and b/_media/pinouts/ESP32-SOLO-1_pinout.jpg differ diff --git a/_media/pinouts/ESP32-S_pinout.jpg b/_media/pinouts/ESP32-S_pinout.jpg new file mode 100644 index 0000000000..a712f81b00 Binary files /dev/null and b/_media/pinouts/ESP32-S_pinout.jpg differ diff --git a/_media/pinouts/ESP32-WROVER_pinout.jpg b/_media/pinouts/ESP32-WROVER_pinout.jpg new file mode 100644 index 0000000000..0518c98da1 Binary files /dev/null and b/_media/pinouts/ESP32-WROVER_pinout.jpg differ diff --git a/_media/pinouts/ESP8266_pinout.jpg b/_media/pinouts/ESP8266_pinout.jpg new file mode 100644 index 0000000000..aa52f48c3a Binary files /dev/null and b/_media/pinouts/ESP8266_pinout.jpg differ diff --git a/_media/pinouts/LM1_pinout.jpg b/_media/pinouts/LM1_pinout.jpg new file mode 100644 index 0000000000..1a49d89ab3 Binary files /dev/null and b/_media/pinouts/LM1_pinout.jpg differ diff --git a/_media/pinouts/LM2_pinout.jpg b/_media/pinouts/LM2_pinout.jpg new file mode 100644 index 0000000000..1811f92611 Binary files /dev/null and b/_media/pinouts/LM2_pinout.jpg differ diff --git a/_media/pinouts/PSF-B85_pinout.jpg b/_media/pinouts/PSF-B85_pinout.jpg new file mode 100644 index 0000000000..d1592652cb Binary files /dev/null and b/_media/pinouts/PSF-B85_pinout.jpg differ diff --git a/_media/pinouts/TYLC4.png b/_media/pinouts/TYLC4.png new file mode 100644 index 0000000000..a2818b8810 Binary files /dev/null and b/_media/pinouts/TYLC4.png differ diff --git a/_media/pinouts/TYLC4_pinout.jpg b/_media/pinouts/TYLC4_pinout.jpg new file mode 100644 index 0000000000..cd56cbfaeb Binary files /dev/null and b/_media/pinouts/TYLC4_pinout.jpg differ diff --git a/_media/pinouts/TYLC5.png b/_media/pinouts/TYLC5.png new file mode 100644 index 0000000000..afe8de6956 Binary files /dev/null and b/_media/pinouts/TYLC5.png differ diff --git a/_media/pinouts/TYLC5_pinout.png b/_media/pinouts/TYLC5_pinout.png new file mode 100644 index 0000000000..ad5e479fad Binary files /dev/null and b/_media/pinouts/TYLC5_pinout.png differ diff --git a/_media/pinouts/TYWE1S_pinout.jpg b/_media/pinouts/TYWE1S_pinout.jpg new file mode 100644 index 0000000000..7cf3c155f9 Binary files /dev/null and b/_media/pinouts/TYWE1S_pinout.jpg differ diff --git a/_media/pinouts/TYWE2L_pinout.jpg b/_media/pinouts/TYWE2L_pinout.jpg new file mode 100644 index 0000000000..bd28431b75 Binary files /dev/null and b/_media/pinouts/TYWE2L_pinout.jpg differ diff --git a/_media/pinouts/TYWE2S_pinout.jpg b/_media/pinouts/TYWE2S_pinout.jpg new file mode 100644 index 0000000000..d5df6c7a32 Binary files /dev/null and b/_media/pinouts/TYWE2S_pinout.jpg differ diff --git a/_media/pinouts/TYWE3L_pinout.jpg b/_media/pinouts/TYWE3L_pinout.jpg new file mode 100644 index 0000000000..0be87a4fdf Binary files /dev/null and b/_media/pinouts/TYWE3L_pinout.jpg differ diff --git a/_media/pinouts/TYWE3S_pinout.jpg b/_media/pinouts/TYWE3S_pinout.jpg new file mode 100644 index 0000000000..1b3973ed42 Binary files /dev/null and b/_media/pinouts/TYWE3S_pinout.jpg differ diff --git a/_media/pinouts/TYWE5P.jpg b/_media/pinouts/TYWE5P.jpg new file mode 100644 index 0000000000..386971b93e Binary files /dev/null and b/_media/pinouts/TYWE5P.jpg differ diff --git a/_media/pinouts/TYWE5P_pinout.jpg b/_media/pinouts/TYWE5P_pinout.jpg new file mode 100644 index 0000000000..ed2a0d15f1 Binary files /dev/null and b/_media/pinouts/TYWE5P_pinout.jpg differ diff --git a/_media/pinouts/WT8266-S1.png b/_media/pinouts/WT8266-S1.png new file mode 100644 index 0000000000..6f98e5b246 Binary files /dev/null and b/_media/pinouts/WT8266-S1.png differ diff --git a/_media/pinouts/sonoff_mini.jpg b/_media/pinouts/sonoff_mini.jpg new file mode 100644 index 0000000000..4049bffa72 Binary files /dev/null and b/_media/pinouts/sonoff_mini.jpg differ diff --git a/_media/pow1.jpg b/_media/pow1.jpg new file mode 100644 index 0000000000..a86e583530 Binary files /dev/null and b/_media/pow1.jpg differ diff --git a/_media/pr_tutorial_1.png b/_media/pr_tutorial_1.png new file mode 100644 index 0000000000..6b858c5738 Binary files /dev/null and b/_media/pr_tutorial_1.png differ diff --git a/_media/pr_tutorial_2.png b/_media/pr_tutorial_2.png new file mode 100644 index 0000000000..c470471797 Binary files /dev/null and b/_media/pr_tutorial_2.png differ diff --git a/_media/pr_tutorial_3.png b/_media/pr_tutorial_3.png new file mode 100644 index 0000000000..e3ea5e738d Binary files /dev/null and b/_media/pr_tutorial_3.png differ diff --git a/_media/pr_tutorial_4.png b/_media/pr_tutorial_4.png new file mode 100644 index 0000000000..d19fa822c1 Binary files /dev/null and b/_media/pr_tutorial_4.png differ diff --git a/_media/prism-http.min.js b/_media/prism-http.min.js new file mode 100644 index 0000000000..860ba54454 --- /dev/null +++ b/_media/prism-http.min.js @@ -0,0 +1,1325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + prism/prism-http.min.js at gh-pages · PrismJS/prism + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Skip to content + + + + + + + + + + + + + +
      + +
      + + +
      + +
      + + + +
      +
      +
      + + + + + + + + + + + + + + + + + +
      +
      + +
        + + + +
      • + + + + +
      • + +
      • + +
        + +
        + + + Watch + + +
        + Notifications +
        +
        + + + + + + + +
        +
        +
        + +
        +
      • + +
      • +
        +
        + + +
        +
        + + +
        + +
      • + +
      • +
        + + Fork + +
        + +

        Fork prism

        +
        +
        + +
        +

        If this dialog fails to load, you can visit the fork page directly.

        +
        +
        +
        +
        + + +
      • +
      + +

      + + /prism + + +

      + +
      + + + + + + +
      +
      +
      + + + + + + + + + Permalink + + + + +
      + + +
      + + Branch: + gh-pages + + + + + + + +
      + +
      + + Find file + + + Copy path + +
      +
      + + +
      + + Find file + + + Copy path + +
      +
      + + + + +
      + + +
      +
      + + 7 contributors + + +
      + +

      + Users who have contributed to this file +

      +
      + +
      +
      + + + @Golmote + + @LeaVerou + + @tstapleton + + @ben-eb + + @lvgunst + + @kbjr + + @danielgtaylor + + + +
      +
      + + + + + +
      + +
      +
      + + 1 lines (1 sloc) + + 905 Bytes +
      + +
      + +
      + Raw + Blame + History +
      + + +
      + + + + +
      + +
      +
      + +
      +
      +
      + + + +
      +
      +
      + + You're using jump to definition to discover and navigate code. +
      + +
      + + + +
      + + + + + + +
      Prism.languages.http={"request-line":{pattern:/^(?:POST|GET|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT)\s(?:https?:\/\/|\/)\S+\sHTTP\/[0-9.]+/m,inside:{property:/^(?:POST|GET|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT)\b/,"attr-name":/:\w+/}},"response-status":{pattern:/^HTTP\/1.[01] \d+.*/m,inside:{property:{pattern:/(^HTTP\/1.[01] )\d+.*/i,lookbehind:!0}}},"header-name":{pattern:/^[\w-]+:(?=.)/m,alias:"keyword"}};var httpLanguages={"application/json":Prism.languages.javascript,"application/xml":Prism.languages.markup,"text/xml":Prism.languages.markup,"text/html":Prism.languages.markup};for(var contentType in httpLanguages)if(httpLanguages[contentType]){var options={};options[contentType]={pattern:new RegExp("(content-type:\\s*"+contentType+"[\\w\\W]*?)(?:\\r?\\n|\\r){2}[\\w\\W]*","i"),lookbehind:!0,inside:{rest:httpLanguages[contentType]}},Prism.languages.insertBefore("http","header-name",options)}
      + + + +
      + +
      + + + +
      + + +
      + + +
      +
      + + + + + +
      +
      + +
      +
      + + +
      + + + + + + +
      + + + You can’t perform that action at this time. +
      + + + + + + + + + + + + + + +
      + + + + diff --git a/_media/projector/ProjectorCtrl_TH16_mounted.jpg b/_media/projector/ProjectorCtrl_TH16_mounted.jpg new file mode 100644 index 0000000000..c945451614 Binary files /dev/null and b/_media/projector/ProjectorCtrl_TH16_mounted.jpg differ diff --git a/_media/projector/ProjectorCtrl_TH16_unboxed.jpg b/_media/projector/ProjectorCtrl_TH16_unboxed.jpg new file mode 100644 index 0000000000..ef7a8761b7 Binary files /dev/null and b/_media/projector/ProjectorCtrl_TH16_unboxed.jpg differ diff --git a/_media/projector/ProjectorCtrl_V300W.jpg b/_media/projector/ProjectorCtrl_V300W.jpg new file mode 100644 index 0000000000..9e3ea6cfdd Binary files /dev/null and b/_media/projector/ProjectorCtrl_V300W.jpg differ diff --git a/_media/pwmdimmer1.jpg b/_media/pwmdimmer1.jpg new file mode 100644 index 0000000000..a4bc817cdd Binary files /dev/null and b/_media/pwmdimmer1.jpg differ diff --git a/_media/pwmdimmer2.jpg b/_media/pwmdimmer2.jpg new file mode 100644 index 0000000000..b384b9fd28 Binary files /dev/null and b/_media/pwmdimmer2.jpg differ diff --git a/_media/pwmdimmer3.jpg b/_media/pwmdimmer3.jpg new file mode 100644 index 0000000000..49bb485cc3 Binary files /dev/null and b/_media/pwmdimmer3.jpg differ diff --git a/_media/pwmdimmer4.jpg b/_media/pwmdimmer4.jpg new file mode 100644 index 0000000000..d65763b89f Binary files /dev/null and b/_media/pwmdimmer4.jpg differ diff --git a/_media/pwmdimmer5.jpg b/_media/pwmdimmer5.jpg new file mode 100644 index 0000000000..b12a3d105e Binary files /dev/null and b/_media/pwmdimmer5.jpg differ diff --git a/_media/pwmdimmer6.jpg b/_media/pwmdimmer6.jpg new file mode 100644 index 0000000000..fba39cb4cd Binary files /dev/null and b/_media/pwmdimmer6.jpg differ diff --git a/_media/range-extender-napt.svg b/_media/range-extender-napt.svg new file mode 100644 index 0000000000..542eae3096 --- /dev/null +++ b/_media/range-extender-napt.svg @@ -0,0 +1,522 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + No need for return route + + ExtenderESP10.99.1.1192.168.1.10 + + AP / Router192.168.1.1 + + + + + RemoteESP10.99.1.100 + + DefaultGateway + + Source IP192.168.1.10 + Extender knows to sendtraffic back to remote ESP + No way to accessweb interface onremote + + diff --git a/_media/range-extender-routing.svg b/_media/range-extender-routing.svg new file mode 100644 index 0000000000..0c8debe1b8 --- /dev/null +++ b/_media/range-extender-routing.svg @@ -0,0 +1,481 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + With RouteAccess 10.99.1.Xvia 192.168.1.10 + + ExtenderESP10.99.1.1192.168.1.10 + + AP / Router192.168.1.1 + + + + + RemoteESP10.99.1.100 + + DefaultGateway + Without Route + + + diff --git a/_media/solax-x1/HW-0519_Breakout.jpg b/_media/solax-x1/HW-0519_Breakout.jpg new file mode 100644 index 0000000000..62adbe4a37 Binary files /dev/null and b/_media/solax-x1/HW-0519_Breakout.jpg differ diff --git a/_media/solax-x1/HW-0519_Breakout_480.jpg b/_media/solax-x1/HW-0519_Breakout_480.jpg new file mode 100644 index 0000000000..1298dbabec Binary files /dev/null and b/_media/solax-x1/HW-0519_Breakout_480.jpg differ diff --git a/_media/solax-x1/RJ45.png b/_media/solax-x1/RJ45.png new file mode 100644 index 0000000000..fd5af6e513 Binary files /dev/null and b/_media/solax-x1/RJ45.png differ diff --git a/_media/solax-x1/SP3485_Breakout1.jpg b/_media/solax-x1/SP3485_Breakout1.jpg new file mode 100644 index 0000000000..ba5a23c21c Binary files /dev/null and b/_media/solax-x1/SP3485_Breakout1.jpg differ diff --git a/_media/solax-x1/SP3485_Breakout1_240.jpg b/_media/solax-x1/SP3485_Breakout1_240.jpg new file mode 100644 index 0000000000..6a49d839e0 Binary files /dev/null and b/_media/solax-x1/SP3485_Breakout1_240.jpg differ diff --git a/_media/solax-x1/SP3485_Breakout2.jpg b/_media/solax-x1/SP3485_Breakout2.jpg new file mode 100644 index 0000000000..7b610c26b4 Binary files /dev/null and b/_media/solax-x1/SP3485_Breakout2.jpg differ diff --git a/_media/solax-x1/SP3485_Breakout2_240.jpg b/_media/solax-x1/SP3485_Breakout2_240.jpg new file mode 100644 index 0000000000..bc6ac2f7d5 Binary files /dev/null and b/_media/solax-x1/SP3485_Breakout2_240.jpg differ diff --git a/_media/solax-x1/SolaxPower_Single_Phase_External_Communication_Protocol_X1_V1.8.pdf b/_media/solax-x1/SolaxPower_Single_Phase_External_Communication_Protocol_X1_V1.8.pdf new file mode 100644 index 0000000000..d08b89d656 Binary files /dev/null and b/_media/solax-x1/SolaxPower_Single_Phase_External_Communication_Protocol_X1_V1.8.pdf differ diff --git a/_media/solax-x1/X1Air.png b/_media/solax-x1/X1Air.png new file mode 100644 index 0000000000..942e1b22b1 Binary files /dev/null and b/_media/solax-x1/X1Air.png differ diff --git a/_media/solax-x1/X1Air_200.png b/_media/solax-x1/X1Air_200.png new file mode 100644 index 0000000000..87fd3f3827 Binary files /dev/null and b/_media/solax-x1/X1Air_200.png differ diff --git a/_media/solax-x1/X1Mini.png b/_media/solax-x1/X1Mini.png new file mode 100644 index 0000000000..fd4458578a Binary files /dev/null and b/_media/solax-x1/X1Mini.png differ diff --git a/_media/solax-x1/X1Mini_200.png b/_media/solax-x1/X1Mini_200.png new file mode 100644 index 0000000000..a94d4a79bb Binary files /dev/null and b/_media/solax-x1/X1Mini_200.png differ diff --git a/_media/solax-x1/X1Smart.png b/_media/solax-x1/X1Smart.png new file mode 100644 index 0000000000..9b73fdced4 Binary files /dev/null and b/_media/solax-x1/X1Smart.png differ diff --git a/_media/solax-x1/X1Smart_200.png b/_media/solax-x1/X1Smart_200.png new file mode 100644 index 0000000000..1758a4a91a Binary files /dev/null and b/_media/solax-x1/X1Smart_200.png differ diff --git a/_media/solax-x1/x1-config-mark_260.png b/_media/solax-x1/x1-config-mark_260.png new file mode 100644 index 0000000000..ba416c516e Binary files /dev/null and b/_media/solax-x1/x1-config-mark_260.png differ diff --git a/_media/solax-x1/x1-example_300.png b/_media/solax-x1/x1-example_300.png new file mode 100644 index 0000000000..baadec2e0e Binary files /dev/null and b/_media/solax-x1/x1-example_300.png differ diff --git a/_media/sps30-interface.png b/_media/sps30-interface.png new file mode 100644 index 0000000000..761035e851 Binary files /dev/null and b/_media/sps30-interface.png differ diff --git a/_media/symbol.svg b/_media/symbol.svg new file mode 100644 index 0000000000..43f86da173 --- /dev/null +++ b/_media/symbol.svg @@ -0,0 +1 @@ +Element 1 \ No newline at end of file diff --git a/_media/taaralabs_MBusUART.png b/_media/taaralabs_MBusUART.png new file mode 100644 index 0000000000..7a344abe2f Binary files /dev/null and b/_media/taaralabs_MBusUART.png differ diff --git a/_media/tasmota-buttons-small.png b/_media/tasmota-buttons-small.png new file mode 100644 index 0000000000..a84ecd9944 Binary files /dev/null and b/_media/tasmota-buttons-small.png differ diff --git a/_media/tasmota-buttons.svg b/_media/tasmota-buttons.svg new file mode 100644 index 0000000000..69818886b8 --- /dev/null +++ b/_media/tasmota-buttons.svg @@ -0,0 +1,111 @@ + + + + + Button_n (external pull up) + All boards are supposed to have those to insure proper boot conditions + No need for external pull-down on GPIO15 + No need for external pull-up on GPIO0 and GPIO2 + GPIO15 + Except : + + + + 4.7 kΩ + GPIOx + + + + +3.3V + + + + + + + + + + + GPIO2 + GPIO0 + Except : + GPIOx + + +3.3V + + + + + + + + 4.7 kΩ + + + + + + + + GPIO2 + GPIO0 + Except : + + + + + + + + GPIOx + + +3.3V + + + + + + + + 4.7 kΩ + Except: GPIO15 + + + + + + + + + + + 4.7 kΩ + GPIOx + + + + +3.3V + + + + Button_in (inverted, external pull down) + Button_i (inverted, internal pull up) + HI when pressed + Normally LOW + Button_in (inverted, external pull down) + Button_n (external pull up) + Button (internal pull up) + LOW when pressed + Normally HI + Open when pressed + Normally Closed + PUSH-TO-BREAK + Closed when pressed + Normally Opened + PUSH-TO-MAKE + + + + + \ No newline at end of file diff --git a/_media/tasmota_integration_showcase.pdn b/_media/tasmota_integration_showcase.pdn new file mode 100644 index 0000000000..b275b8a3e0 Binary files /dev/null and b/_media/tasmota_integration_showcase.pdn differ diff --git a/_media/tasmota_integration_showcase.png b/_media/tasmota_integration_showcase.png new file mode 100644 index 0000000000..600e00791a Binary files /dev/null and b/_media/tasmota_integration_showcase.png differ diff --git a/_media/tasmotizer1.png b/_media/tasmotizer1.png new file mode 100644 index 0000000000..e3646fc587 Binary files /dev/null and b/_media/tasmotizer1.png differ diff --git a/_media/tasmotizer2.png b/_media/tasmotizer2.png new file mode 100644 index 0000000000..686cf3b4d2 Binary files /dev/null and b/_media/tasmotizer2.png differ diff --git a/_media/tasmotizer3.png b/_media/tasmotizer3.png new file mode 100644 index 0000000000..6bf2c115df Binary files /dev/null and b/_media/tasmotizer3.png differ diff --git a/_media/tdm-retained.png b/_media/tdm-retained.png new file mode 100644 index 0000000000..1987d15fec Binary files /dev/null and b/_media/tdm-retained.png differ diff --git a/_media/teleinfo/teleinfo_en_sch.png b/_media/teleinfo/teleinfo_en_sch.png new file mode 100644 index 0000000000..2bb48b067f Binary files /dev/null and b/_media/teleinfo/teleinfo_en_sch.png differ diff --git a/_media/teleinfo/teleinfo_energy_display.png b/_media/teleinfo/teleinfo_energy_display.png new file mode 100644 index 0000000000..3afd28bce1 Binary files /dev/null and b/_media/teleinfo/teleinfo_energy_display.png differ diff --git a/_media/teleinfo/teleinfo_esp32_en_gpio4.png b/_media/teleinfo/teleinfo_esp32_en_gpio4.png new file mode 100644 index 0000000000..7077363228 Binary files /dev/null and b/_media/teleinfo/teleinfo_esp32_en_gpio4.png differ diff --git a/_media/teleinfo/teleinfo_esp32_rx_gpio33.png b/_media/teleinfo/teleinfo_esp32_rx_gpio33.png new file mode 100644 index 0000000000..a6aeda09c8 Binary files /dev/null and b/_media/teleinfo/teleinfo_esp32_rx_gpio33.png differ diff --git a/_media/teleinfo/teleinfo_esp8266_en.png b/_media/teleinfo/teleinfo_esp8266_en.png new file mode 100644 index 0000000000..aea918a0fe Binary files /dev/null and b/_media/teleinfo/teleinfo_esp8266_en.png differ diff --git a/_media/teleinfo/teleinfo_esp8266_rx_d3.png b/_media/teleinfo/teleinfo_esp8266_rx_d3.png new file mode 100644 index 0000000000..ddcbfd5715 Binary files /dev/null and b/_media/teleinfo/teleinfo_esp8266_rx_d3.png differ diff --git a/_media/teleinfo/teleinfo_esp8266_rx_d7.png b/_media/teleinfo/teleinfo_esp8266_rx_d7.png new file mode 100644 index 0000000000..247ff03df5 Binary files /dev/null and b/_media/teleinfo/teleinfo_esp8266_rx_d7.png differ diff --git a/_media/teleinfo/teleinfo_pi_shield.png b/_media/teleinfo/teleinfo_pi_shield.png new file mode 100644 index 0000000000..b5a5b3d687 Binary files /dev/null and b/_media/teleinfo/teleinfo_pi_shield.png differ diff --git a/_media/teleinfo/teleinfo_sch.png b/_media/teleinfo/teleinfo_sch.png new file mode 100644 index 0000000000..6dfd501244 Binary files /dev/null and b/_media/teleinfo/teleinfo_sch.png differ diff --git a/_media/teleinfo/teleinfo_tic_shield.png b/_media/teleinfo/teleinfo_tic_shield.png new file mode 100644 index 0000000000..d8a23eca8e Binary files /dev/null and b/_media/teleinfo/teleinfo_tic_shield.png differ diff --git a/_media/template2.png b/_media/template2.png new file mode 100644 index 0000000000..7f70bae642 Binary files /dev/null and b/_media/template2.png differ diff --git a/_media/template4.png b/_media/template4.png new file mode 100644 index 0000000000..5ffbf4f592 Binary files /dev/null and b/_media/template4.png differ diff --git a/_media/template5.png b/_media/template5.png new file mode 100644 index 0000000000..4ca0a212dc Binary files /dev/null and b/_media/template5.png differ diff --git a/_media/templatemodule.png b/_media/templatemodule.png new file mode 100644 index 0000000000..da0003dc94 Binary files /dev/null and b/_media/templatemodule.png differ diff --git a/_media/themes/dark.png b/_media/themes/dark.png new file mode 100644 index 0000000000..7efb9f96e0 Binary files /dev/null and b/_media/themes/dark.png differ diff --git a/_media/themes/halloween.png b/_media/themes/halloween.png new file mode 100644 index 0000000000..37394fe01f Binary files /dev/null and b/_media/themes/halloween.png differ diff --git a/_media/themes/light.png b/_media/themes/light.png new file mode 100644 index 0000000000..76c112e910 Binary files /dev/null and b/_media/themes/light.png differ diff --git a/_media/themes/navy.png b/_media/themes/navy.png new file mode 100644 index 0000000000..75641d2c08 Binary files /dev/null and b/_media/themes/navy.png differ diff --git a/_media/themes/purple_rain.png b/_media/themes/purple_rain.png new file mode 100644 index 0000000000..f61b4fe820 Binary files /dev/null and b/_media/themes/purple_rain.png differ diff --git a/_media/themes/solarized_dark.png b/_media/themes/solarized_dark.png new file mode 100644 index 0000000000..98af6f72d1 Binary files /dev/null and b/_media/themes/solarized_dark.png differ diff --git a/_media/thermostat/Feature_rich_thermostat.png b/_media/thermostat/Feature_rich_thermostat.png new file mode 100644 index 0000000000..8c5987ee5c Binary files /dev/null and b/_media/thermostat/Feature_rich_thermostat.png differ diff --git a/_media/thermostat/busch_jaeger.jpg b/_media/thermostat/busch_jaeger.jpg new file mode 100644 index 0000000000..a8aba32128 Binary files /dev/null and b/_media/thermostat/busch_jaeger.jpg differ diff --git a/_media/thermostat/bypass_thermostat.png b/_media/thermostat/bypass_thermostat.png new file mode 100644 index 0000000000..5c0c8f7112 Binary files /dev/null and b/_media/thermostat/bypass_thermostat.png differ diff --git a/_media/thermostat/conventional_thermostat.png b/_media/thermostat/conventional_thermostat.png new file mode 100644 index 0000000000..fda0476acc Binary files /dev/null and b/_media/thermostat/conventional_thermostat.png differ diff --git a/_media/thermostat/multi_thermostat.png b/_media/thermostat/multi_thermostat.png new file mode 100644 index 0000000000..e47cd04f0f Binary files /dev/null and b/_media/thermostat/multi_thermostat.png differ diff --git a/_media/thermostat/sensors.png b/_media/thermostat/sensors.png new file mode 100644 index 0000000000..58b6024f6a Binary files /dev/null and b/_media/thermostat/sensors.png differ diff --git a/_media/ufs_file_editor.png b/_media/ufs_file_editor.png new file mode 100644 index 0000000000..ca2e86972d Binary files /dev/null and b/_media/ufs_file_editor.png differ diff --git a/_media/ufs_manage_file_system.png b/_media/ufs_manage_file_system.png new file mode 100644 index 0000000000..7d2806c0f4 Binary files /dev/null and b/_media/ufs_manage_file_system.png differ diff --git a/_media/ufs_menu_manage.png b/_media/ufs_menu_manage.png new file mode 100644 index 0000000000..02c034dcae Binary files /dev/null and b/_media/ufs_menu_manage.png differ diff --git a/_media/upgrading_2.png b/_media/upgrading_2.png new file mode 100644 index 0000000000..5b27b8aa46 Binary files /dev/null and b/_media/upgrading_2.png differ diff --git a/_media/usb-to-ttl-ch340g.jpg b/_media/usb-to-ttl-ch340g.jpg new file mode 100644 index 0000000000..a2458ab2f7 Binary files /dev/null and b/_media/usb-to-ttl-ch340g.jpg differ diff --git a/_media/web_installer.jpg b/_media/web_installer.jpg new file mode 100644 index 0000000000..1f185c6649 Binary files /dev/null and b/_media/web_installer.jpg differ diff --git a/_media/web_installer_2.jpg b/_media/web_installer_2.jpg new file mode 100644 index 0000000000..3edea6d180 Binary files /dev/null and b/_media/web_installer_2.jpg differ diff --git a/_media/web_installer_3.jpg b/_media/web_installer_3.jpg new file mode 100644 index 0000000000..c612ae7b82 Binary files /dev/null and b/_media/web_installer_3.jpg differ diff --git a/_media/web_installer_4.jpg b/_media/web_installer_4.jpg new file mode 100644 index 0000000000..ac969a415b Binary files /dev/null and b/_media/web_installer_4.jpg differ diff --git a/_media/web_installer_config1.jpg b/_media/web_installer_config1.jpg new file mode 100644 index 0000000000..30d1d69da3 Binary files /dev/null and b/_media/web_installer_config1.jpg differ diff --git a/_media/web_installer_config2.jpg b/_media/web_installer_config2.jpg new file mode 100644 index 0000000000..79967325d9 Binary files /dev/null and b/_media/web_installer_config2.jpg differ diff --git a/_media/web_installer_config3.jpg b/_media/web_installer_config3.jpg new file mode 100644 index 0000000000..205c2683a9 Binary files /dev/null and b/_media/web_installer_config3.jpg differ diff --git a/_media/web_installer_config4.jpg b/_media/web_installer_config4.jpg new file mode 100644 index 0000000000..6ffb30a1e0 Binary files /dev/null and b/_media/web_installer_config4.jpg differ diff --git a/_media/web_installer_config5.jpg b/_media/web_installer_config5.jpg new file mode 100644 index 0000000000..bb18f1fc20 Binary files /dev/null and b/_media/web_installer_config5.jpg differ diff --git a/_media/wemos_sht30_config_marked.jpg b/_media/wemos_sht30_config_marked.jpg new file mode 100644 index 0000000000..133eb23290 Binary files /dev/null and b/_media/wemos_sht30_config_marked.jpg differ diff --git a/_media/wemos_sht30_main_marked.jpg b/_media/wemos_sht30_main_marked.jpg new file mode 100644 index 0000000000..cb5a0d4e9f Binary files /dev/null and b/_media/wemos_sht30_main_marked.jpg differ diff --git a/_media/wificonfig1.jpg b/_media/wificonfig1.jpg new file mode 100644 index 0000000000..fa71868f46 Binary files /dev/null and b/_media/wificonfig1.jpg differ diff --git a/_media/wificonfig2.jpg b/_media/wificonfig2.jpg new file mode 100644 index 0000000000..3f4d89c178 Binary files /dev/null and b/_media/wificonfig2.jpg differ diff --git a/_media/wificonfig3.jpg b/_media/wificonfig3.jpg new file mode 100644 index 0000000000..ee87a6f2f6 Binary files /dev/null and b/_media/wificonfig3.jpg differ diff --git a/_media/wificonfig4.jpg b/_media/wificonfig4.jpg new file mode 100644 index 0000000000..12b1458c50 Binary files /dev/null and b/_media/wificonfig4.jpg differ diff --git a/_media/wificonfig5.jpg b/_media/wificonfig5.jpg new file mode 100644 index 0000000000..6d8b468a15 Binary files /dev/null and b/_media/wificonfig5.jpg differ diff --git a/_media/wificonfig6.jpg b/_media/wificonfig6.jpg new file mode 100644 index 0000000000..80ffecb130 Binary files /dev/null and b/_media/wificonfig6.jpg differ diff --git a/_media/ws2813b_config.png b/_media/ws2813b_config.png new file mode 100644 index 0000000000..ebd34b4fcf Binary files /dev/null and b/_media/ws2813b_config.png differ diff --git a/_media/ws2813b_main.png b/_media/ws2813b_main.png new file mode 100644 index 0000000000..c07eef982c Binary files /dev/null and b/_media/ws2813b_main.png differ diff --git a/_media/ws281x.png b/_media/ws281x.png new file mode 100644 index 0000000000..6924ee9927 Binary files /dev/null and b/_media/ws281x.png differ diff --git a/_media/xsns_83_neopool_config.png b/_media/xsns_83_neopool_config.png new file mode 100644 index 0000000000..e88d938759 Binary files /dev/null and b/_media/xsns_83_neopool_config.png differ diff --git a/_media/xsns_83_neopool_connector.jpg b/_media/xsns_83_neopool_connector.jpg new file mode 100644 index 0000000000..e8836d95c7 Binary files /dev/null and b/_media/xsns_83_neopool_connector.jpg differ diff --git a/_media/xsns_83_neopool_connector_wifi.jpg b/_media/xsns_83_neopool_connector_wifi.jpg new file mode 100644 index 0000000000..86de501ae6 Binary files /dev/null and b/_media/xsns_83_neopool_connector_wifi.jpg differ diff --git a/_media/xsns_83_neopool_ctrl.png b/_media/xsns_83_neopool_ctrl.png new file mode 100644 index 0000000000..e1f23877b5 Binary files /dev/null and b/_media/xsns_83_neopool_ctrl.png differ diff --git a/_media/xsns_83_neopool_do_ctrl.png b/_media/xsns_83_neopool_do_ctrl.png new file mode 100644 index 0000000000..eb49d5bf54 Binary files /dev/null and b/_media/xsns_83_neopool_do_ctrl.png differ diff --git a/_media/xsns_83_neopool_gui.png b/_media/xsns_83_neopool_gui.png new file mode 100644 index 0000000000..d585600823 Binary files /dev/null and b/_media/xsns_83_neopool_gui.png differ diff --git a/_media/xsns_83_neopool_rs485_1_s.png b/_media/xsns_83_neopool_rs485_1_s.png new file mode 100644 index 0000000000..e153e1a597 Binary files /dev/null and b/_media/xsns_83_neopool_rs485_1_s.png differ diff --git a/_media/xsns_83_neopool_rs485_2_s.png b/_media/xsns_83_neopool_rs485_2_s.png new file mode 100644 index 0000000000..64419066e0 Binary files /dev/null and b/_media/xsns_83_neopool_rs485_2_s.png differ diff --git a/_media/xsns_83_neopool_s.png b/_media/xsns_83_neopool_s.png new file mode 100644 index 0000000000..c8d654c087 Binary files /dev/null and b/_media/xsns_83_neopool_s.png differ diff --git a/_media/xsns_83_neopool_schematic.png b/_media/xsns_83_neopool_schematic.png new file mode 100644 index 0000000000..ea0b5879fc Binary files /dev/null and b/_media/xsns_83_neopool_schematic.png differ diff --git a/_media/xsns_83_neopool_timer.png b/_media/xsns_83_neopool_timer.png new file mode 100644 index 0000000000..ac3633ed0e Binary files /dev/null and b/_media/xsns_83_neopool_timer.png differ diff --git a/_media/zigbee/IKEA_LED1545G12.jpg b/_media/zigbee/IKEA_LED1545G12.jpg new file mode 100644 index 0000000000..48bcd3e1ac Binary files /dev/null and b/_media/zigbee/IKEA_LED1545G12.jpg differ diff --git a/_media/zigbee/IKEA_Remote.jpg b/_media/zigbee/IKEA_Remote.jpg new file mode 100644 index 0000000000..99d266e5b6 Binary files /dev/null and b/_media/zigbee/IKEA_Remote.jpg differ diff --git a/_media/zigbee/OSRAM_Plug_AB3257001NJ.jpg b/_media/zigbee/OSRAM_Plug_AB3257001NJ.jpg new file mode 100644 index 0000000000..508bce4072 Binary files /dev/null and b/_media/zigbee/OSRAM_Plug_AB3257001NJ.jpg differ diff --git a/_media/zigbee/OSRAM_Switch_mini.jpg b/_media/zigbee/OSRAM_Switch_mini.jpg new file mode 100644 index 0000000000..b519b9291d Binary files /dev/null and b/_media/zigbee/OSRAM_Switch_mini.jpg differ diff --git a/_media/zigbee/Philips_motion_sensor_SML001.jpg b/_media/zigbee/Philips_motion_sensor_SML001.jpg new file mode 100644 index 0000000000..89762d1e1f Binary files /dev/null and b/_media/zigbee/Philips_motion_sensor_SML001.jpg differ diff --git a/_media/zigbee/Serial.png b/_media/zigbee/Serial.png new file mode 100644 index 0000000000..9df4bc475f Binary files /dev/null and b/_media/zigbee/Serial.png differ diff --git a/_media/zigbee/Serial_ko.png b/_media/zigbee/Serial_ko.png new file mode 100644 index 0000000000..2738bb92e8 Binary files /dev/null and b/_media/zigbee/Serial_ko.png differ diff --git a/_media/zigbee/Serial_ok.png b/_media/zigbee/Serial_ok.png new file mode 100644 index 0000000000..a70e989bc4 Binary files /dev/null and b/_media/zigbee/Serial_ok.png differ diff --git a/_media/zigbee/Serial_tasmota.png b/_media/zigbee/Serial_tasmota.png new file mode 100644 index 0000000000..758c36debe Binary files /dev/null and b/_media/zigbee/Serial_tasmota.png differ diff --git a/_media/zigbee/Z-Stack_API_1_2.pdf b/_media/zigbee/Z-Stack_API_1_2.pdf new file mode 100644 index 0000000000..0f3e544a20 Binary files /dev/null and b/_media/zigbee/Z-Stack_API_1_2.pdf differ diff --git a/_media/zigbee/Z2T.jpg b/_media/zigbee/Z2T.jpg new file mode 100644 index 0000000000..435f07f7c1 Binary files /dev/null and b/_media/zigbee/Z2T.jpg differ diff --git a/_media/zigbee/cc2531_solderpads.jpg b/_media/zigbee/cc2531_solderpads.jpg new file mode 100644 index 0000000000..b19b67be3d Binary files /dev/null and b/_media/zigbee/cc2531_solderpads.jpg differ diff --git a/_media/zigbee/cc2531usb.jpg b/_media/zigbee/cc2531usb.jpg new file mode 100644 index 0000000000..d5baf3745e Binary files /dev/null and b/_media/zigbee/cc2531usb.jpg differ diff --git a/_media/zigbee/superhouse-z2t.jpg b/_media/zigbee/superhouse-z2t.jpg new file mode 100644 index 0000000000..b5a8fbbd55 Binary files /dev/null and b/_media/zigbee/superhouse-z2t.jpg differ diff --git a/_media/zigbeeinwebui.jpg b/_media/zigbeeinwebui.jpg new file mode 100644 index 0000000000..472e1f0e2e Binary files /dev/null and b/_media/zigbeeinwebui.jpg differ diff --git a/assets/css/anchor-fix.css b/assets/css/anchor-fix.css new file mode 100644 index 0000000000..78c9f26f32 --- /dev/null +++ b/assets/css/anchor-fix.css @@ -0,0 +1,6 @@ +.cmnd:target { + display: block; + height: 4rem; /*same height as header*/ + margin-top: -4rem; /*same height as header*/ + visibility: hidden; +} diff --git a/assets/css/dark-theme.css b/assets/css/dark-theme.css new file mode 100644 index 0000000000..6e89b9f20a --- /dev/null +++ b/assets/css/dark-theme.css @@ -0,0 +1,232 @@ +html, +body { + background-color: #282b36; + color: #f8f8f2; +} + +html .md-nav--primary .md-nav__title~.md-nav__list { + background-color: #282b36; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + color: #f8f8f2 !important; +} + +code { + background-color: #4e4e4e !important; + color: #d9d9d9 !important; + box-shadow: 0em 0 0 #4e4e4e, 0em 0 0 #4e4e4e !important; + padding-left: 5px !important; + padding-right: 5px !important; +} + +blockquote { + color: #e4e4e4 !important; +} + +.md-nav__link[data-md-state=blur] { + color: #b1b1b1a1; +} + +.md-search__scrollwrap { + background-color: #6c6d73 !important; +} + +.md-search-result__article--document:before { + color: #f8f8f2 !important; +} + +.md-search-result__meta { + color: #f8f8f2 !important; +} + +.md-search-result__teaser { + color: rgba(255, 255, 255, 0.57) !important; +} + +.md-typeset .task-list-control [type=checkbox]:checked+.task-list-indicator:before, +.md-typeset .task-list-control .task-list-indicator:before { + color: #f8f8f2 !important; +} + +.md-typeset .admonition, +.md-typeset .md-typeset__table table { + background-color: #00000052; +} + +html body .md-typeset .md-source-file { + color: #f8f8f2 !important; + background-color: #585858 !important; +} + +.md-typeset mark { + background-color: rgba(255, 235, 59, 0.73) !important; + box-shadow: 0.25em 0 0 rgba(255, 235, 59, 0.73), -0.25em 0 0 rgba(255, 235, 59, 0.73) !important; +} + +.md-typeset del.critic { + background-color: #9e0000 !important; + box-shadow: 0.25em 0 0 #9e0000, -0.25em 0 0 #9e0000 !important; +} + +.md-typeset ins.critic { + background-color: #008e00 !important; + box-shadow: 0.25em 0 0 #008e00, -0.25em 0 0 #008e00 !important; +} + +.md-typeset .critic.comment { + color: #000000 !important; +} + +.md-typeset .critic.comment:before { + color: #000000 !important; +} + +/* Small devices */ +@media only screen and (max-width: 76.1875em) { + .md-nav { + background-color: #282b36; + } + + html .md-nav--primary .md-nav__title, + html .md-nav--primary .md-nav__title:before { + color: #f8f8f2 !important; + } +} + +/* Medium devices */ +@media only screen and (min-width: 60em) { + [data-md-toggle=search]:checked~.md-header .md-search__input { + background-color: #b1b1b13b !important; + color: #fff !important; + } + + .md-search__icon[for=__search]:before, + .md-search__icon[for=__search]:after { + color: #fff; + } +} + +.md-typeset .footnote { + color: rgba(255, 255, 255, 0.78) !important; +} + +.md-footer-nav { + background-color: rgba(19, 19, 19, 0.64) !important; +} + +.md-typeset .codehilitetable .linenodiv, .md-typeset .highlighttable .linenodiv { + background-color: #585858 !important; + color: white !important; +} + +[data-md-color-primary=indigo] .md-typeset a { + color: #5e71dc !important; +} + +[data-md-color-primary=purple] .md-typeset a { + color: #c352d6 !important; +} + +[data-md-color-primary=deep-purple] .md-typeset a { + color: #9a6bea !important; +} + +[data-md-color-primary=brown] .md-typeset a { + color: #c58d78 !important; +} + +[data-md-color-primary=grey] .md-typeset a { + color: #a9a7a7 !important; +} + +[data-md-color-primary=blue-grey] .md-typeset a { + color: #678592 !important; +} + +/* Code block changes taken from: https://github.com/jwarby/jekyll-pygments-themes */ +.codehilite pre{ background-color: #272822 !important; color: #d9d9d9; } +.codehilite .hll { background-color: #272822; } +.codehilite .c { color: #75715e } /* Comment */ +.codehilite .err { color: #960050; background-color: #1e0010 } /* Error */ +.codehilite .k { color: #66d9ef } /* Keyword */ +.codehilite .l { color: #ae81ff } /* Literal */ +.codehilite .n { color: #f8f8f2 } /* Name */ +.codehilite .o { color: #f92672 } /* Operator */ +.codehilite .p { color: #f8f8f2 } /* Punctuation */ +.codehilite .cm { color: #75715e } /* Comment.Multiline */ +.codehilite .cp { color: #75715e } /* Comment.Preproc */ +.codehilite .c1 { color: #75715e } /* Comment.Single */ +.codehilite .cs { color: #75715e } /* Comment.Special */ +.codehilite .ge { font-style: italic } /* Generic.Emph */ +.codehilite .gs { font-weight: bold } /* Generic.Strong */ +.codehilite .kc { color: #66d9ef } /* Keyword.Constant */ +.codehilite .kd { color: #66d9ef } /* Keyword.Declaration */ +.codehilite .kn { color: #f92672 } /* Keyword.Namespace */ +.codehilite .kp { color: #66d9ef } /* Keyword.Pseudo */ +.codehilite .kr { color: #66d9ef } /* Keyword.Reserved */ +.codehilite .kt { color: #66d9ef } /* Keyword.Type */ +.codehilite .ld { color: #e6db74 } /* Literal.Date */ +.codehilite .m { color: #ae81ff } /* Literal.Number */ +.codehilite .s { color: #e6db74 } /* Literal.String */ +.codehilite .na { color: #a6e22e } /* Name.Attribute */ +.codehilite .nb { color: #f8f8f2 } /* Name.Builtin */ +.codehilite .nc { color: #a6e22e } /* Name.Class */ +.codehilite .no { color: #66d9ef } /* Name.Constant */ +.codehilite .nd { color: #a6e22e } /* Name.Decorator */ +.codehilite .ni { color: #f8f8f2 } /* Name.Entity */ +.codehilite .ne { color: #a6e22e } /* Name.Exception */ +.codehilite .nf { color: #a6e22e } /* Name.Function */ +.codehilite .nl { color: #f8f8f2 } /* Name.Label */ +.codehilite .nn { color: #f8f8f2 } /* Name.Namespace */ +.codehilite .nx { color: #a6e22e } /* Name.Other */ +.codehilite .py { color: #f8f8f2 } /* Name.Property */ +.codehilite .nt { color: #f92672 } /* Name.Tag */ +.codehilite .nv { color: #f8f8f2 } /* Name.Variable */ +.codehilite .ow { color: #f92672 } /* Operator.Word */ +.codehilite .w { color: #f8f8f2 } /* Text.Whitespace */ +.codehilite .mf { color: #ae81ff } /* Literal.Number.Float */ +.codehilite .mh { color: #ae81ff } /* Literal.Number.Hex */ +.codehilite .mi { color: #ae81ff } /* Literal.Number.Integer */ +.codehilite .mo { color: #ae81ff } /* Literal.Number.Oct */ +.codehilite .sb { color: #e6db74 } /* Literal.String.Backtick */ +.codehilite .sc { color: #e6db74 } /* Literal.String.Char */ +.codehilite .sd { color: #e6db74 } /* Literal.String.Doc */ +.codehilite .s2 { color: #e6db74 } /* Literal.String.Double */ +.codehilite .se { color: #ae81ff } /* Literal.String.Escape */ +.codehilite .sh { color: #e6db74 } /* Literal.String.Heredoc */ +.codehilite .si { color: #e6db74 } /* Literal.String.Interpol */ +.codehilite .sx { color: #e6db74 } /* Literal.String.Other */ +.codehilite .sr { color: #e6db74 } /* Literal.String.Regex */ +.codehilite .s1 { color: #e6db74 } /* Literal.String.Single */ +.codehilite .ss { color: #e6db74 } /* Literal.String.Symbol */ +.codehilite .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ +.codehilite .vc { color: #f8f8f2 } /* Name.Variable.Class */ +.codehilite .vg { color: #f8f8f2 } /* Name.Variable.Global */ +.codehilite .vi { color: #f8f8f2 } /* Name.Variable.Instance */ +.codehilite .il { color: #ae81ff } /* Literal.Number.Integer.Long */ +.codehilite .gh { } /* Generic Heading & Diff Header */ +.codehilite .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */ +.codehilite .gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */ +.codehilite .gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */ + +.codehilite .gd, +.md-typeset .highlight .gd { + background-color: #6b0e0e66; + color: #ff5a5a; +} + +.codehilite .gi, +.md-typeset .highlight .gi { + background-color: #2b3a2b; +} + +.md-typeset .superfences-content, +.md-typeset .codehilite { + background-color: #0000 !important; +} \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000000..1cf13b9f9d Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.dff1b7c8.min.js b/assets/javascripts/bundle.dff1b7c8.min.js new file mode 100644 index 0000000000..a89e799ad1 --- /dev/null +++ b/assets/javascripts/bundle.dff1b7c8.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var gi=Object.create;var dr=Object.defineProperty;var xi=Object.getOwnPropertyDescriptor;var yi=Object.getOwnPropertyNames,Ht=Object.getOwnPropertySymbols,Ei=Object.getPrototypeOf,hr=Object.prototype.hasOwnProperty,Xr=Object.prototype.propertyIsEnumerable;var Jr=(e,t,r)=>t in e?dr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,I=(e,t)=>{for(var r in t||(t={}))hr.call(t,r)&&Jr(e,r,t[r]);if(Ht)for(var r of Ht(t))Xr.call(t,r)&&Jr(e,r,t[r]);return e};var Zr=(e,t)=>{var r={};for(var o in e)hr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Ht)for(var o of Ht(e))t.indexOf(o)<0&&Xr.call(e,o)&&(r[o]=e[o]);return r};var br=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var wi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of yi(t))!hr.call(e,n)&&n!==r&&dr(e,n,{get:()=>t[n],enumerable:!(o=xi(t,n))||o.enumerable});return e};var $t=(e,t,r)=>(r=e!=null?gi(Ei(e)):{},wi(t||!e||!e.__esModule?dr(r,"default",{value:e,enumerable:!0}):r,e));var to=br((vr,eo)=>{(function(e,t){typeof vr=="object"&&typeof eo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(vr,function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(A){return!!(A&&A!==document&&A.nodeName!=="HTML"&&A.nodeName!=="BODY"&&"classList"in A&&"contains"in A.classList)}function c(A){var it=A.type,Ne=A.tagName;return!!(Ne==="INPUT"&&s[it]&&!A.readOnly||Ne==="TEXTAREA"&&!A.readOnly||A.isContentEditable)}function p(A){A.classList.contains("focus-visible")||(A.classList.add("focus-visible"),A.setAttribute("data-focus-visible-added",""))}function m(A){A.hasAttribute("data-focus-visible-added")&&(A.classList.remove("focus-visible"),A.removeAttribute("data-focus-visible-added"))}function f(A){A.metaKey||A.altKey||A.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(A){o=!1}function d(A){a(A.target)&&(o||c(A.target))&&p(A.target)}function b(A){a(A.target)&&(A.target.classList.contains("focus-visible")||A.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),m(A.target))}function _(A){document.visibilityState==="hidden"&&(n&&(o=!0),re())}function re(){document.addEventListener("mousemove",Y),document.addEventListener("mousedown",Y),document.addEventListener("mouseup",Y),document.addEventListener("pointermove",Y),document.addEventListener("pointerdown",Y),document.addEventListener("pointerup",Y),document.addEventListener("touchmove",Y),document.addEventListener("touchstart",Y),document.addEventListener("touchend",Y)}function Z(){document.removeEventListener("mousemove",Y),document.removeEventListener("mousedown",Y),document.removeEventListener("mouseup",Y),document.removeEventListener("pointermove",Y),document.removeEventListener("pointerdown",Y),document.removeEventListener("pointerup",Y),document.removeEventListener("touchmove",Y),document.removeEventListener("touchstart",Y),document.removeEventListener("touchend",Y)}function Y(A){A.target.nodeName&&A.target.nodeName.toLowerCase()==="html"||(o=!1,Z())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",_,!0),re(),r.addEventListener("focus",d,!0),r.addEventListener("blur",b,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var Vr=br((Mt,Dr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Mt=="object"&&typeof Dr=="object"?Dr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Mt=="object"?Mt.ClipboardJS=r():t.ClipboardJS=r()})(Mt,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return vi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),m=i(817),f=i.n(m);function u(F){try{return document.execCommand(F)}catch(S){return!1}}var d=function(S){var y=f()(S);return u("cut"),y},b=d;function _(F){var S=document.documentElement.getAttribute("dir")==="rtl",y=document.createElement("textarea");y.style.fontSize="12pt",y.style.border="0",y.style.padding="0",y.style.margin="0",y.style.position="absolute",y.style[S?"right":"left"]="-9999px";var R=window.pageYOffset||document.documentElement.scrollTop;return y.style.top="".concat(R,"px"),y.setAttribute("readonly",""),y.value=F,y}var re=function(S,y){var R=_(S);y.container.appendChild(R);var P=f()(R);return u("copy"),R.remove(),P},Z=function(S){var y=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},R="";return typeof S=="string"?R=re(S,y):S instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(S==null?void 0:S.type)?R=re(S.value,y):(R=f()(S),u("copy")),R},Y=Z;function A(F){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?A=function(y){return typeof y}:A=function(y){return y&&typeof Symbol=="function"&&y.constructor===Symbol&&y!==Symbol.prototype?"symbol":typeof y},A(F)}var it=function(){var S=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},y=S.action,R=y===void 0?"copy":y,P=S.container,q=S.target,Me=S.text;if(R!=="copy"&&R!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&A(q)==="object"&&q.nodeType===1){if(R==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(R==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Me)return Y(Me,{container:P});if(q)return R==="cut"?b(q):Y(q,{container:P})},Ne=it;function Ie(F){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Ie=function(y){return typeof y}:Ie=function(y){return y&&typeof Symbol=="function"&&y.constructor===Symbol&&y!==Symbol.prototype?"symbol":typeof y},Ie(F)}function pi(F,S){if(!(F instanceof S))throw new TypeError("Cannot call a class as a function")}function Gr(F,S){for(var y=0;y0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof P.action=="function"?P.action:this.defaultAction,this.target=typeof P.target=="function"?P.target:this.defaultTarget,this.text=typeof P.text=="function"?P.text:this.defaultText,this.container=Ie(P.container)==="object"?P.container:document.body}},{key:"listenClick",value:function(P){var q=this;this.listener=p()(P,"click",function(Me){return q.onClick(Me)})}},{key:"onClick",value:function(P){var q=P.delegateTarget||P.currentTarget,Me=this.action(q)||"copy",kt=Ne({action:Me,container:this.container,target:this.target(q),text:this.text(q)});this.emit(kt?"success":"error",{action:Me,text:kt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(P){return ur("action",P)}},{key:"defaultTarget",value:function(P){var q=ur("target",P);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(P){return ur("text",P)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(P){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return Y(P,q)}},{key:"cut",value:function(P){return b(P)}},{key:"isSupported",value:function(){var P=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof P=="string"?[P]:P,Me=!!document.queryCommandSupported;return q.forEach(function(kt){Me=Me&&!!document.queryCommandSupported(kt)}),Me}}]),y}(a()),vi=bi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s},438:function(o,n,i){var s=i(828);function a(m,f,u,d,b){var _=p.apply(this,arguments);return m.addEventListener(u,_,b),{destroy:function(){m.removeEventListener(u,_,b)}}}function c(m,f,u,d,b){return typeof m.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof m=="string"&&(m=document.querySelectorAll(m)),Array.prototype.map.call(m,function(_){return a(_,f,u,d,b)}))}function p(m,f,u,d){return function(b){b.delegateTarget=s(b.target,f),b.delegateTarget&&d.call(m,b)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(o,n,i){var s=i(879),a=i(438);function c(u,d,b){if(!u&&!d&&!b)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(b))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,b);if(s.nodeList(u))return m(u,d,b);if(s.string(u))return f(u,d,b);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,b){return u.addEventListener(d,b),{destroy:function(){u.removeEventListener(d,b)}}}function m(u,d,b){return Array.prototype.forEach.call(u,function(_){_.addEventListener(d,b)}),{destroy:function(){Array.prototype.forEach.call(u,function(_){_.removeEventListener(d,b)})}}}function f(u,d,b){return a(document.body,u,d,b)}o.exports=c},817:function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var _a=/["'&<>]/;Pn.exports=Aa;function Aa(e){var t=""+e,r=_a.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function U(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||a(u,d)})})}function a(u,d){try{c(o[u](d))}catch(b){f(i[0][3],b)}}function c(u){u.value instanceof Ze?Promise.resolve(u.value.v).then(p,m):f(i[0][2],u)}function p(u){a("next",u)}function m(u){a("throw",u)}function f(u,d){u(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function no(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Ee=="function"?Ee(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function C(e){return typeof e=="function"}function at(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var It=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function De(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Pe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Ee(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(_){t={error:_}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var m=this.initialTeardown;if(C(m))try{m()}catch(_){i=_ instanceof It?_.errors:[_]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=Ee(f),d=u.next();!d.done;d=u.next()){var b=d.value;try{io(b)}catch(_){i=i!=null?i:[],_ instanceof It?i=D(D([],U(i)),U(_.errors)):i.push(_)}}}catch(_){o={error:_}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new It(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)io(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&De(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&De(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var xr=Pe.EMPTY;function Pt(e){return e instanceof Pe||e&&"closed"in e&&C(e.remove)&&C(e.add)&&C(e.unsubscribe)}function io(e){C(e)?e():e.unsubscribe()}var Le={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?xr:(this.currentObservers=null,a.push(r),new Pe(function(){o.currentObservers=null,De(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new j;return r.source=this,r},t.create=function(r,o){return new uo(r,o)},t}(j);var uo=function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:xr},t}(x);var yt={now:function(){return(yt.delegate||Date).now()},delegate:void 0};var Et=function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=yt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=mt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(mt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(Wt);var vo=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(Ut);var Te=new vo(bo);var T=new j(function(e){return e.complete()});function Nt(e){return e&&C(e.schedule)}function Mr(e){return e[e.length-1]}function Qe(e){return C(Mr(e))?e.pop():void 0}function Oe(e){return Nt(Mr(e))?e.pop():void 0}function Dt(e,t){return typeof Mr(e)=="number"?e.pop():t}var lt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Vt(e){return C(e==null?void 0:e.then)}function zt(e){return C(e[pt])}function qt(e){return Symbol.asyncIterator&&C(e==null?void 0:e[Symbol.asyncIterator])}function Kt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function ki(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Qt=ki();function Yt(e){return C(e==null?void 0:e[Qt])}function Bt(e){return oo(this,arguments,function(){var r,o,n,i;return Rt(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,Ze(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,Ze(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,Ze(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Gt(e){return C(e==null?void 0:e.getReader)}function W(e){if(e instanceof j)return e;if(e!=null){if(zt(e))return Hi(e);if(lt(e))return $i(e);if(Vt(e))return Ri(e);if(qt(e))return go(e);if(Yt(e))return Ii(e);if(Gt(e))return Pi(e)}throw Kt(e)}function Hi(e){return new j(function(t){var r=e[pt]();if(C(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function $i(e){return new j(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?L(function(n,i){return e(n,i,o)}):de,ge(1),r?He(t):Io(function(){return new Xt}))}}function Po(){for(var e=[],t=0;t=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var m,f,u,d=0,b=!1,_=!1,re=function(){f==null||f.unsubscribe(),f=void 0},Z=function(){re(),m=u=void 0,b=_=!1},Y=function(){var A=m;Z(),A==null||A.unsubscribe()};return g(function(A,it){d++,!_&&!b&&re();var Ne=u=u!=null?u:r();it.add(function(){d--,d===0&&!_&&!b&&(f=kr(Y,c))}),Ne.subscribe(it),!m&&d>0&&(m=new tt({next:function(Ie){return Ne.next(Ie)},error:function(Ie){_=!0,re(),f=kr(Z,n,Ie),Ne.error(Ie)},complete:function(){b=!0,re(),f=kr(Z,s),Ne.complete()}}),W(A).subscribe(m))})(p)}}function kr(e,t){for(var r=[],o=2;oe.next(document)),e}function z(e,t=document){return Array.from(t.querySelectorAll(e))}function N(e,t=document){let r=ce(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ce(e,t=document){return t.querySelector(e)||void 0}function Re(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}var ea=M(h(document.body,"focusin"),h(document.body,"focusout")).pipe(ke(1),V(void 0),l(()=>Re()||document.body),B(1));function er(e){return ea.pipe(l(t=>e.contains(t)),G())}function Je(e){return{x:e.offsetLeft,y:e.offsetTop}}function Uo(e){return M(h(window,"load"),h(window,"resize")).pipe(Ae(0,Te),l(()=>Je(e)),V(Je(e)))}function tr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return M(h(e,"scroll"),h(window,"resize")).pipe(Ae(0,Te),l(()=>tr(e)),V(tr(e)))}function No(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)No(e,r)}function O(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)No(o,n);return o}function rr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function ht(e){let t=O("script",{src:e});return $(()=>(document.head.appendChild(t),M(h(t,"load"),h(t,"error").pipe(v(()=>St(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),k(()=>document.head.removeChild(t)),ge(1))))}var Do=new x,ta=$(()=>typeof ResizeObserver=="undefined"?ht("https://unpkg.com/resize-observer-polyfill"):H(void 0)).pipe(l(()=>new ResizeObserver(e=>{for(let t of e)Do.next(t)})),v(e=>M(Ve,H(e)).pipe(k(()=>e.disconnect()))),B(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function xe(e){return ta.pipe(w(t=>t.observe(e)),v(t=>Do.pipe(L(({target:r})=>r===e),k(()=>t.unobserve(e)),l(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function or(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var Vo=new x,ra=$(()=>H(new IntersectionObserver(e=>{for(let t of e)Vo.next(t)},{threshold:0}))).pipe(v(e=>M(Ve,H(e)).pipe(k(()=>e.disconnect()))),B(1));function nr(e){return ra.pipe(w(t=>t.observe(e)),v(t=>Vo.pipe(L(({target:r})=>r===e),k(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function zo(e,t=16){return dt(e).pipe(l(({y:r})=>{let o=he(e),n=bt(e);return r>=n.height-o.height-t}),G())}var ir={drawer:N("[data-md-toggle=drawer]"),search:N("[data-md-toggle=search]")};function qo(e){return ir[e].checked}function Ke(e,t){ir[e].checked!==t&&ir[e].click()}function We(e){let t=ir[e];return h(t,"change").pipe(l(()=>t.checked),V(t.checked))}function oa(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function na(){return M(h(window,"compositionstart").pipe(l(()=>!0)),h(window,"compositionend").pipe(l(()=>!1))).pipe(V(!1))}function Ko(){let e=h(window,"keydown").pipe(L(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:qo("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),L(({mode:t,type:r})=>{if(t==="global"){let o=Re();if(typeof o!="undefined")return!oa(o,r)}return!0}),le());return na().pipe(v(t=>t?T:e))}function fe(){return new URL(location.href)}function ot(e){location.href=e.href}function Qo(){return new x}function Yo(){return location.hash.slice(1)}function Pr(e){let t=O("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function ia(e){return M(h(window,"hashchange"),e).pipe(l(Yo),V(Yo()),L(t=>t.length>0),B(1))}function Bo(e){return ia(e).pipe(l(t=>ce(`[id="${t}"]`)),L(t=>typeof t!="undefined"))}function Fr(e){let t=matchMedia(e);return Zt(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function Go(){let e=matchMedia("print");return M(h(window,"beforeprint").pipe(l(()=>!0)),h(window,"afterprint").pipe(l(()=>!1))).pipe(V(e.matches))}function jr(e,t){return e.pipe(v(r=>r?t():T))}function ar(e,t={credentials:"same-origin"}){return me(fetch(`${e}`,t)).pipe(pe(()=>T),v(r=>r.status!==200?St(()=>new Error(r.statusText)):H(r)))}function Ue(e,t){return ar(e,t).pipe(v(r=>r.json()),B(1))}function Jo(e,t){let r=new DOMParser;return ar(e,t).pipe(v(o=>o.text()),l(o=>r.parseFromString(o,"text/xml")),B(1))}function Xo(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function Zo(){return M(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(l(Xo),V(Xo()))}function en(){return{width:innerWidth,height:innerHeight}}function tn(){return h(window,"resize",{passive:!0}).pipe(l(en),V(en()))}function rn(){return Q([Zo(),tn()]).pipe(l(([e,t])=>({offset:e,size:t})),B(1))}function sr(e,{viewport$:t,header$:r}){let o=t.pipe(X("size")),n=Q([o,r]).pipe(l(()=>Je(e)));return Q([r,t,n]).pipe(l(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function aa(e){return h(e,"message",t=>t.data)}function sa(e){let t=new x;return t.subscribe(r=>e.postMessage(r)),t}function on(e,t=new Worker(e)){let r=aa(t),o=sa(t),n=new x;n.subscribe(o);let i=o.pipe(J(),ee(!0));return n.pipe(J(),qe(r.pipe(K(i))),le())}var ca=N("#__config"),vt=JSON.parse(ca.textContent);vt.base=`${new URL(vt.base,fe())}`;function ue(){return vt}function te(e){return vt.features.includes(e)}function be(e,t){return typeof t!="undefined"?vt.translations[e].replace("#",t.toString()):vt.translations[e]}function ye(e,t=document){return N(`[data-md-component=${e}]`,t)}function ne(e,t=document){return z(`[data-md-component=${e}]`,t)}function pa(e){let t=N(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(l(()=>N(".md-typeset",e)),l(r=>({hash:__md_hash(r.innerHTML)})))}function nn(e){if(!te("announce.dismiss")||!e.childElementCount)return T;if(!e.hidden){let t=N(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return $(()=>{let t=new x;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),pa(e).pipe(w(r=>t.next(r)),k(()=>t.complete()),l(r=>I({ref:e},r)))})}function ma(e,{target$:t}){return t.pipe(l(r=>({hidden:r!==e})))}function an(e,t){let r=new x;return r.subscribe(({hidden:o})=>{e.hidden=o}),ma(e,t).pipe(w(o=>r.next(o)),k(()=>r.complete()),l(o=>I({ref:e},o)))}function la(e,t){let r=$(()=>Q([Uo(e),dt(t)])).pipe(l(([{x:o,y:n},i])=>{let{width:s,height:a}=he(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return er(e).pipe(v(o=>r.pipe(l(n=>({active:o,offset:n})),ge(+!o||1/0))))}function sn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return $(()=>{let i=new x,s=i.pipe(J(),ee(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),nr(e).pipe(K(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),M(i.pipe(L(({active:a})=>a)),i.pipe(ke(250),L(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Ae(16,Te)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(Rr(125,Te),L(()=>!!e.offsetParent),l(()=>e.offsetParent.getBoundingClientRect()),l(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(K(s),L(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(K(s),oe(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let m=e.parentElement.closest(".md-annotation");m instanceof HTMLElement?m.focus():(p=Re())==null||p.blur()}}),r.pipe(K(s),L(a=>a===o),ze(125)).subscribe(()=>e.focus()),la(e,t).pipe(w(a=>i.next(a)),k(()=>i.complete()),l(a=>I({ref:e},a)))})}function Wr(e){return O("div",{class:"md-tooltip",id:e},O("div",{class:"md-tooltip__inner md-typeset"}))}function cn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return O("aside",{class:"md-annotation",tabIndex:0},Wr(t),O("a",{href:r,class:"md-annotation__index",tabIndex:-1},O("span",{"data-md-annotation-id":e})))}else return O("aside",{class:"md-annotation",tabIndex:0},Wr(t),O("span",{class:"md-annotation__index",tabIndex:-1},O("span",{"data-md-annotation-id":e})))}function pn(e){return O("button",{class:"md-clipboard md-icon",title:be("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function Ur(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,O("del",null,p)," "],[]).slice(0,-1),i=ue(),s=new URL(e.location,i.base);te("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=ue();return O("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},O("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&O("div",{class:"md-search-result__icon md-icon"}),r>0&&O("h1",null,e.title),r<=0&&O("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return O("span",{class:`md-tag ${p}`},c)}),o>0&&n.length>0&&O("p",{class:"md-search-result__terms"},be("search.result.term.missing"),": ",...n)))}function mn(e){let t=e[0].score,r=[...e],o=ue(),n=r.findIndex(m=>!`${new URL(m.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(m=>m.scoreUr(m,1)),...c.length?[O("details",{class:"md-search-result__more"},O("summary",{tabIndex:-1},O("div",null,c.length>0&&c.length===1?be("search.result.more.one"):be("search.result.more.other",c.length))),...c.map(m=>Ur(m,1)))]:[]];return O("li",{class:"md-search-result__item"},p)}function ln(e){return O("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>O("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?rr(r):r)))}function Nr(e){let t=`tabbed-control tabbed-control--${e}`;return O("div",{class:t,hidden:!0},O("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function fn(e){return O("div",{class:"md-typeset__scrollwrap"},O("div",{class:"md-typeset__table"},e))}function fa(e){let t=ue(),r=new URL(`../${e.version}/`,t.base);return O("li",{class:"md-version__item"},O("a",{href:`${r}`,class:"md-version__link"},e.title))}function un(e,t){return O("div",{class:"md-version"},O("button",{class:"md-version__current","aria-label":be("select.version")},t.title),O("ul",{class:"md-version__list"},e.map(fa)))}function ua(e){return e.tagName==="CODE"?z(".c, .c1, .cm",e):[e]}function da(e){let t=[];for(let r of ua(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function dn(e,t){t.append(...Array.from(e.childNodes))}function cr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of da(t)){let[,c]=a.textContent.match(/\((\d+)\)/);ce(`:scope > li:nth-child(${c})`,e)&&(s.set(c,cn(c,i)),a.replaceWith(s.get(c)))}return s.size===0?T:$(()=>{let a=new x,c=a.pipe(J(),ee(!0)),p=[];for(let[m,f]of s)p.push([N(".md-typeset",f),N(`:scope > li:nth-child(${m})`,e)]);return o.pipe(K(c)).subscribe(m=>{e.hidden=!m,e.classList.toggle("md-annotation-list",m);for(let[f,u]of p)m?dn(f,u):dn(u,f)}),M(...[...s].map(([,m])=>sn(m,t,{target$:r}))).pipe(k(()=>a.complete()),le())})}function hn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return hn(t)}}function bn(e,t){return $(()=>{let r=hn(e);return typeof r!="undefined"?cr(r,e,t):T})}var gn=$t(Vr());var ha=0;function xn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return xn(t)}}function vn(e){return xe(e).pipe(l(({width:t})=>({scrollable:bt(e).width>t})),X("scrollable"))}function yn(e,t){let{matches:r}=matchMedia("(hover)"),o=$(()=>{let n=new x;if(n.subscribe(({scrollable:s})=>{s&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")}),gn.default.isSupported()&&(e.closest(".copy")||te("content.code.copy")&&!e.closest(".no-copy"))){let s=e.closest("pre");s.id=`__code_${ha++}`,s.insertBefore(pn(s.id),e)}let i=e.closest(".highlight");if(i instanceof HTMLElement){let s=xn(i);if(typeof s!="undefined"&&(i.classList.contains("annotate")||te("content.code.annotate"))){let a=cr(s,e,t);return vn(e).pipe(w(c=>n.next(c)),k(()=>n.complete()),l(c=>I({ref:e},c)),qe(xe(i).pipe(l(({width:c,height:p})=>c&&p),G(),v(c=>c?a:T))))}}return vn(e).pipe(w(s=>n.next(s)),k(()=>n.complete()),l(s=>I({ref:e},s)))});return te("content.lazy")?nr(e).pipe(L(n=>n),ge(1),v(()=>o)):o}function ba(e,{target$:t,print$:r}){let o=!0;return M(t.pipe(l(n=>n.closest("details:not([open])")),L(n=>e===n),l(()=>({action:"open",reveal:!0}))),r.pipe(L(n=>n||!o),w(()=>o=e.open),l(n=>({action:n?"open":"close"}))))}function En(e,t){return $(()=>{let r=new x;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),ba(e,t).pipe(w(o=>r.next(o)),k(()=>r.complete()),l(o=>I({ref:e},o)))})}var wn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var zr,ga=0;function xa(){return typeof mermaid=="undefined"||mermaid instanceof Element?ht("https://unpkg.com/mermaid@9.4.3/dist/mermaid.min.js"):H(void 0)}function Sn(e){return e.classList.remove("mermaid"),zr||(zr=xa().pipe(w(()=>mermaid.initialize({startOnLoad:!1,themeCSS:wn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),l(()=>{}),B(1))),zr.subscribe(()=>{e.classList.add("mermaid");let t=`__mermaid_${ga++}`,r=O("div",{class:"mermaid"}),o=e.textContent;mermaid.mermaidAPI.render(t,o,(n,i)=>{let s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})}),zr.pipe(l(()=>({ref:e})))}var Tn=O("table");function On(e){return e.replaceWith(Tn),Tn.replaceWith(fn(e)),H({ref:e})}function ya(e){let t=z(":scope > input",e),r=t.find(o=>o.checked)||t[0];return M(...t.map(o=>h(o,"change").pipe(l(()=>N(`label[for="${o.id}"]`))))).pipe(V(N(`label[for="${r.id}"]`)),l(o=>({active:o})))}function Mn(e,{viewport$:t}){let r=Nr("prev");e.append(r);let o=Nr("next");e.append(o);let n=N(".tabbed-labels",e);return $(()=>{let i=new x,s=i.pipe(J(),ee(!0));return Q([i,xe(e)]).pipe(Ae(1,Te),K(s)).subscribe({next([{active:a},c]){let p=Je(a),{width:m}=he(a);e.style.setProperty("--md-indicator-x",`${p.x}px`),e.style.setProperty("--md-indicator-width",`${m}px`);let f=tr(n);(p.xf.x+c.width)&&n.scrollTo({left:Math.max(0,p.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),Q([dt(n),xe(n)]).pipe(K(s)).subscribe(([a,c])=>{let p=bt(n);r.hidden=a.x<16,o.hidden=a.x>p.width-c.width-16}),M(h(r,"click").pipe(l(()=>-1)),h(o,"click").pipe(l(()=>1))).pipe(K(s)).subscribe(a=>{let{width:c}=he(n);n.scrollBy({left:c*a,behavior:"smooth"})}),te("content.tabs.link")&&i.pipe(je(1),oe(t)).subscribe(([{active:a},{offset:c}])=>{let p=a.innerText.trim();if(a.hasAttribute("data-md-switching"))a.removeAttribute("data-md-switching");else{let m=e.offsetTop-c.y;for(let u of z("[data-tabs]"))for(let d of z(":scope > input",u)){let b=N(`label[for="${d.id}"]`);if(b!==a&&b.innerText.trim()===p){b.setAttribute("data-md-switching",""),d.click();break}}window.scrollTo({top:e.offsetTop-m});let f=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([p,...f])])}}),i.pipe(K(s)).subscribe(()=>{for(let a of z("audio, video",e))a.pause()}),ya(e).pipe(w(a=>i.next(a)),k(()=>i.complete()),l(a=>I({ref:e},a)))}).pipe(rt(ae))}function Ln(e,{viewport$:t,target$:r,print$:o}){return M(...z(".annotate:not(.highlight)",e).map(n=>bn(n,{target$:r,print$:o})),...z("pre:not(.mermaid) > code",e).map(n=>yn(n,{target$:r,print$:o})),...z("pre.mermaid",e).map(n=>Sn(n)),...z("table:not([class])",e).map(n=>On(n)),...z("details",e).map(n=>En(n,{target$:r,print$:o})),...z("[data-tabs]",e).map(n=>Mn(n,{viewport$:t})))}function Ea(e,{alert$:t}){return t.pipe(v(r=>M(H(!0),H(!1).pipe(ze(2e3))).pipe(l(o=>({message:r,active:o})))))}function _n(e,t){let r=N(".md-typeset",e);return $(()=>{let o=new x;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ea(e,t).pipe(w(n=>o.next(n)),k(()=>o.complete()),l(n=>I({ref:e},n)))})}function wa({viewport$:e}){if(!te("header.autohide"))return H(!1);let t=e.pipe(l(({offset:{y:n}})=>n),Ce(2,1),l(([n,i])=>[nMath.abs(i-n.y)>100),l(([,[n]])=>n),G()),o=We("search");return Q([e,o]).pipe(l(([{offset:n},i])=>n.y>400&&!i),G(),v(n=>n?r:H(!1)),V(!1))}function An(e,t){return $(()=>Q([xe(e),wa(t)])).pipe(l(([{height:r},o])=>({height:r,hidden:o})),G((r,o)=>r.height===o.height&&r.hidden===o.hidden),B(1))}function Cn(e,{header$:t,main$:r}){return $(()=>{let o=new x,n=o.pipe(J(),ee(!0));return o.pipe(X("active"),Ge(t)).subscribe(([{active:i},{hidden:s}])=>{e.classList.toggle("md-header--shadow",i&&!s),e.hidden=s}),r.subscribe(o),t.pipe(K(n),l(i=>I({ref:e},i)))})}function Sa(e,{viewport$:t,header$:r}){return sr(e,{viewport$:t,header$:r}).pipe(l(({offset:{y:o}})=>{let{height:n}=he(e);return{active:o>=n}}),X("active"))}function kn(e,t){return $(()=>{let r=new x;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=ce(".md-content h1");return typeof o=="undefined"?T:Sa(o,t).pipe(w(n=>r.next(n)),k(()=>r.complete()),l(n=>I({ref:e},n)))})}function Hn(e,{viewport$:t,header$:r}){let o=r.pipe(l(({height:i})=>i),G()),n=o.pipe(v(()=>xe(e).pipe(l(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),X("bottom"))));return Q([o,n,t]).pipe(l(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),G((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function Ta(e){let t=__md_get("__palette")||{index:e.findIndex(r=>matchMedia(r.getAttribute("data-md-color-media")).matches)};return H(...e).pipe(se(r=>h(r,"change").pipe(l(()=>r))),V(e[Math.max(0,t.index)]),l(r=>({index:e.indexOf(r),color:{scheme:r.getAttribute("data-md-color-scheme"),primary:r.getAttribute("data-md-color-primary"),accent:r.getAttribute("data-md-color-accent")}})),B(1))}function $n(e){let t=O("meta",{name:"theme-color"});document.head.appendChild(t);let r=O("meta",{name:"color-scheme"});return document.head.appendChild(r),$(()=>{let o=new x;o.subscribe(i=>{document.body.setAttribute("data-md-color-switching","");for(let[s,a]of Object.entries(i.color))document.body.setAttribute(`data-md-color-${s}`,a);for(let s=0;s{let i=ye("header"),s=window.getComputedStyle(i);return r.content=s.colorScheme,s.backgroundColor.match(/\d+/g).map(a=>(+a).toString(16).padStart(2,"0")).join("")})).subscribe(i=>t.content=`#${i}`),o.pipe(_e(ae)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")});let n=z("input",e);return Ta(n).pipe(w(i=>o.next(i)),k(()=>o.complete()),l(i=>I({ref:e},i)))})}var qr=$t(Vr());function Oa(e){e.setAttribute("data-md-copying","");let t=e.innerText;return e.removeAttribute("data-md-copying"),t}function Rn({alert$:e}){qr.default.isSupported()&&new j(t=>{new qr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||Oa(N(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(w(t=>{t.trigger.focus()}),l(()=>be("clipboard.copied"))).subscribe(e)}function Ma(e){if(e.length<2)return[""];let[t,r]=[...e].sort((n,i)=>n.length-i.length).map(n=>n.replace(/[^/]+$/,"")),o=0;if(t===r)o=t.length;else for(;t.charCodeAt(o)===r.charCodeAt(o);)o++;return e.map(n=>n.replace(t.slice(0,o),""))}function pr(e){let t=__md_get("__sitemap",sessionStorage,e);if(t)return H(t);{let r=ue();return Jo(new URL("sitemap.xml",e||r.base)).pipe(l(o=>Ma(z("loc",o).map(n=>n.textContent))),pe(()=>T),He([]),w(o=>__md_set("__sitemap",o,sessionStorage,e)))}}function In({location$:e,viewport$:t}){let r=ue();if(location.protocol==="file:")return T;let o=pr().pipe(l(p=>p.map(m=>`${new URL(m,r.base)}`))),n=h(document.body,"click").pipe(oe(o),v(([p,m])=>{if(!(p.target instanceof Element))return T;let f=p.target.closest("a");if(f===null)return T;if(f.target||p.metaKey||p.ctrlKey)return T;let u=new URL(f.href);return u.search=u.hash="",m.includes(`${u}`)?(p.preventDefault(),H(new URL(f.href))):T}),le());n.pipe(ge(1)).subscribe(()=>{let p=ce("link[rel=icon]");typeof p!="undefined"&&(p.href=p.href)}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),n.pipe(oe(t)).subscribe(([p,{offset:m}])=>{history.scrollRestoration="manual",history.replaceState(m,""),history.pushState(null,"",p)}),n.subscribe(e);let i=e.pipe(V(fe()),X("pathname"),je(1),v(p=>ar(p).pipe(pe(()=>(ot(p),T))))),s=new DOMParser,a=i.pipe(v(p=>p.text()),v(p=>{let m=s.parseFromString(p,"text/html");for(let u of["title","link[rel=canonical]","meta[name=author]","meta[name=description]","[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...te("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let d=ce(u),b=ce(u,m);typeof d!="undefined"&&typeof b!="undefined"&&d.replaceWith(b)}let f=ye("container");return Fe(z("script",f)).pipe(v(u=>{let d=m.createElement("script");if(u.src){for(let b of u.getAttributeNames())d.setAttribute(b,u.getAttribute(b));return u.replaceWith(d),new j(b=>{d.onload=()=>b.complete()})}else return d.textContent=u.textContent,u.replaceWith(d),T}),J(),ee(m))}),le());return h(window,"popstate").pipe(l(fe)).subscribe(e),e.pipe(V(fe()),Ce(2,1),v(([p,m])=>p.pathname===m.pathname&&p.hash!==m.hash?H(m):T)).subscribe(p=>{var m,f;history.state!==null||!p.hash?window.scrollTo(0,(f=(m=history.state)==null?void 0:m.y)!=null?f:0):(history.scrollRestoration="auto",Pr(p.hash),history.scrollRestoration="manual")}),a.pipe(oe(e)).subscribe(([,p])=>{var m,f;history.state!==null||!p.hash?window.scrollTo(0,(f=(m=history.state)==null?void 0:m.y)!=null?f:0):Pr(p.hash)}),a.pipe(v(()=>t),X("offset"),ke(100)).subscribe(({offset:p})=>{history.replaceState(p,"")}),a}var jn=$t(Fn());function Wn(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,jn.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function Lt(e){return e.type===1}function mr(e){return e.type===3}function Un(e,t){let r=on(e);return M(H(location.protocol!=="file:"),We("search")).pipe($e(o=>o),v(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:te("search.suggest")}}})),r}function Nn({document$:e}){let t=ue(),r=Ue(new URL("../versions.json",t.base)).pipe(pe(()=>T)),o=r.pipe(l(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(l(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),v(n=>h(document.body,"click").pipe(L(i=>!i.metaKey&&!i.ctrlKey),oe(o),v(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?T:(i.preventDefault(),H(c))}}return T}),v(i=>{let{version:s}=n.get(i);return pr(new URL(i)).pipe(l(a=>{let p=fe().href.replace(t.base,"");return a.includes(p.split("#")[0])?new URL(`../${s}/${p}`,t.base):new URL(i)}))})))).subscribe(n=>ot(n)),Q([r,o]).subscribe(([n,i])=>{N(".md-header__topic").appendChild(un(n,i))}),e.pipe(v(()=>o)).subscribe(n=>{var s;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let a=((s=t.version)==null?void 0:s.default)||"latest";Array.isArray(a)||(a=[a]);e:for(let c of a)for(let p of n.aliases)if(new RegExp(c,"i").test(p)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let a of ne("outdated"))a.hidden=!1})}function ka(e,{worker$:t}){let{searchParams:r}=fe();r.has("q")&&(Ke("search",!0),e.value=r.get("q"),e.focus(),We("search").pipe($e(i=>!i)).subscribe(()=>{let i=new URL(location.href);i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=er(e),n=M(t.pipe($e(Lt)),h(e,"keyup"),o).pipe(l(()=>e.value),G());return Q([n,o]).pipe(l(([i,s])=>({value:i,focus:s})),B(1))}function Dn(e,{worker$:t}){let r=new x,o=r.pipe(J(),ee(!0));Q([t.pipe($e(Lt)),r],(i,s)=>s).pipe(X("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(X("focus")).subscribe(({focus:i})=>{i&&Ke("search",i)}),h(e.form,"reset").pipe(K(o)).subscribe(()=>e.focus());let n=N("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ka(e,{worker$:t}).pipe(w(i=>r.next(i)),k(()=>r.complete()),l(i=>I({ref:e},i)),B(1))}function Vn(e,{worker$:t,query$:r}){let o=new x,n=zo(e.parentElement).pipe(L(Boolean)),i=e.parentElement,s=N(":scope > :first-child",e),a=N(":scope > :last-child",e);We("search").subscribe(m=>a.setAttribute("role",m?"list":"presentation")),o.pipe(oe(r),Hr(t.pipe($e(Lt)))).subscribe(([{items:m},{value:f}])=>{switch(m.length){case 0:s.textContent=f.length?be("search.result.none"):be("search.result.placeholder");break;case 1:s.textContent=be("search.result.one");break;default:let u=rr(m.length);s.textContent=be("search.result.other",u)}});let c=o.pipe(w(()=>a.innerHTML=""),v(({items:m})=>M(H(...m.slice(0,10)),H(...m.slice(10)).pipe(Ce(4),Ir(n),v(([f])=>f)))),l(mn),le());return c.subscribe(m=>a.appendChild(m)),c.pipe(se(m=>{let f=ce("details",m);return typeof f=="undefined"?T:h(f,"toggle").pipe(K(o),l(()=>f))})).subscribe(m=>{m.open===!1&&m.offsetTop<=i.scrollTop&&i.scrollTo({top:m.offsetTop})}),t.pipe(L(mr),l(({data:m})=>m)).pipe(w(m=>o.next(m)),k(()=>o.complete()),l(m=>I({ref:e},m)))}function Ha(e,{query$:t}){return t.pipe(l(({value:r})=>{let o=fe();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function zn(e,t){let r=new x,o=r.pipe(J(),ee(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(K(o)).subscribe(n=>n.preventDefault()),Ha(e,t).pipe(w(n=>r.next(n)),k(()=>r.complete()),l(n=>I({ref:e},n)))}function qn(e,{worker$:t,keyboard$:r}){let o=new x,n=ye("search-query"),i=M(h(n,"keydown"),h(n,"focus")).pipe(_e(ae),l(()=>n.value),G());return o.pipe(Ge(i),l(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let m=a[a.length-1];m.startsWith(p[p.length-1])&&(p[p.length-1]=m)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(L(({mode:a})=>a==="search")).subscribe(a=>{switch(a.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(L(mr),l(({data:a})=>a)).pipe(w(a=>o.next(a)),k(()=>o.complete()),l(()=>({ref:e})))}function Kn(e,{index$:t,keyboard$:r}){let o=ue();try{let n=Un(o.search,t),i=ye("search-query",e),s=ye("search-result",e);h(e,"click").pipe(L(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>Ke("search",!1)),r.pipe(L(({mode:c})=>c==="search")).subscribe(c=>{let p=Re();switch(c.type){case"Enter":if(p===i){let m=new Map;for(let f of z(":first-child [href]",s)){let u=f.firstElementChild;m.set(f,parseFloat(u.getAttribute("data-md-score")))}if(m.size){let[[f]]=[...m].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":Ke("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let m=[i,...z(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,m.indexOf(p))+m.length+(c.type==="ArrowUp"?-1:1))%m.length);m[f].focus()}c.claim();break;default:i!==Re()&&i.focus()}}),r.pipe(L(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Dn(i,{worker$:n});return M(a,Vn(s,{worker$:n,query$:a})).pipe(qe(...ne("search-share",e).map(c=>zn(c,{query$:a})),...ne("search-suggest",e).map(c=>qn(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,Ve}}function Qn(e,{index$:t,location$:r}){return Q([t,r.pipe(V(fe()),L(o=>!!o.searchParams.get("h")))]).pipe(l(([o,n])=>Wn(o.config)(n.searchParams.get("h"))),l(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=O("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function $a(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return Q([r,t]).pipe(l(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),G((i,s)=>i.height===s.height&&i.locked===s.locked))}function Kr(e,o){var n=o,{header$:t}=n,r=Zr(n,["header$"]);let i=N(".md-sidebar__scrollwrap",e),{y:s}=Je(i);return $(()=>{let a=new x,c=a.pipe(J(),ee(!0)),p=a.pipe(Ae(0,Te));return p.pipe(oe(t)).subscribe({next([{height:m},{height:f}]){i.style.height=`${m-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe($e()).subscribe(()=>{for(let m of z(".md-nav__link--active[href]",e)){let f=or(m);if(typeof f!="undefined"){let u=m.offsetTop-f.offsetTop,{height:d}=he(f);f.scrollTo({top:u-d/2})}}}),me(z("label[tabindex]",e)).pipe(se(m=>h(m,"click").pipe(l(()=>m),K(c)))).subscribe(m=>{let f=N(`[id="${m.htmlFor}"]`);N(`[aria-labelledby="${m.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),$a(e,r).pipe(w(m=>a.next(m)),k(()=>a.complete()),l(m=>I({ref:e},m)))})}function Yn(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return Tt(Ue(`${r}/releases/latest`).pipe(pe(()=>T),l(o=>({version:o.tag_name})),He({})),Ue(r).pipe(pe(()=>T),l(o=>({stars:o.stargazers_count,forks:o.forks_count})),He({}))).pipe(l(([o,n])=>I(I({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return Ue(r).pipe(l(o=>({repositories:o.public_repos})),He({}))}}function Bn(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return Ue(r).pipe(pe(()=>T),l(({star_count:o,forks_count:n})=>({stars:o,forks:n})),He({}))}function Gn(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Yn(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return Bn(r,o)}return T}var Ra;function Ia(e){return Ra||(Ra=$(()=>{let t=__md_get("__source",sessionStorage);if(t)return H(t);if(ne("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return T}return Gn(e.href).pipe(w(o=>__md_set("__source",o,sessionStorage)))}).pipe(pe(()=>T),L(t=>Object.keys(t).length>0),l(t=>({facts:t})),B(1)))}function Jn(e){let t=N(":scope > :last-child",e);return $(()=>{let r=new x;return r.subscribe(({facts:o})=>{t.appendChild(ln(o)),t.classList.add("md-source__repository--active")}),Ia(e).pipe(w(o=>r.next(o)),k(()=>r.complete()),l(o=>I({ref:e},o)))})}function Pa(e,{viewport$:t,header$:r}){return xe(document.body).pipe(v(()=>sr(e,{header$:r,viewport$:t})),l(({offset:{y:o}})=>({hidden:o>=10})),X("hidden"))}function Xn(e,t){return $(()=>{let r=new x;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(te("navigation.tabs.sticky")?H({hidden:!1}):Pa(e,t)).pipe(w(o=>r.next(o)),k(()=>r.complete()),l(o=>I({ref:e},o)))})}function Fa(e,{viewport$:t,header$:r}){let o=new Map,n=z("[href^=\\#]",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=ce(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(X("height"),l(({height:a})=>{let c=ye("main"),p=N(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return xe(document.body).pipe(X("height"),v(a=>$(()=>{let c=[];return H([...o].reduce((p,[m,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,m]].reverse(),u)},new Map))}).pipe(l(c=>new Map([...c].sort(([,p],[,m])=>p-m))),Ge(i),v(([c,p])=>t.pipe(Cr(([m,f],{offset:{y:u},size:d})=>{let b=u+d.height>=Math.floor(a.height);for(;f.length;){let[,_]=f[0];if(_-p=u&&!b)f=[m.pop(),...f];else break}return[m,f]},[[],[...c]]),G((m,f)=>m[0]===f[0]&&m[1]===f[1])))))).pipe(l(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),V({prev:[],next:[]}),Ce(2,1),l(([a,c])=>a.prev.length{let i=new x,s=i.pipe(J(),ee(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[m]]of a.entries())m.classList.add("md-nav__link--passed"),m.classList.toggle("md-nav__link--active",p===a.length-1)}),te("toc.follow")){let a=M(t.pipe(ke(1),l(()=>{})),t.pipe(ke(250),l(()=>"smooth")));i.pipe(L(({prev:c})=>c.length>0),Ge(o.pipe(_e(ae))),oe(a)).subscribe(([[{prev:c}],p])=>{let[m]=c[c.length-1];if(m.offsetHeight){let f=or(m);if(typeof f!="undefined"){let u=m.offsetTop-f.offsetTop,{height:d}=he(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return te("navigation.tracking")&&t.pipe(K(s),X("offset"),ke(250),je(1),K(n.pipe(je(1))),Ot({delay:250}),oe(i)).subscribe(([,{prev:a}])=>{let c=fe(),p=a[a.length-1];if(p&&p.length){let[m]=p,{hash:f}=new URL(m.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),Fa(e,{viewport$:t,header$:r}).pipe(w(a=>i.next(a)),k(()=>i.complete()),l(a=>I({ref:e},a)))})}function ja(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(l(({offset:{y:s}})=>s),Ce(2,1),l(([s,a])=>s>a&&a>0),G()),i=r.pipe(l(({active:s})=>s));return Q([i,n]).pipe(l(([s,a])=>!(s&&a)),G(),K(o.pipe(je(1))),ee(!0),Ot({delay:250}),l(s=>({hidden:s})))}function ei(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new x,s=i.pipe(J(),ee(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(K(s),X("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),ja(e,{viewport$:t,main$:o,target$:n}).pipe(w(a=>i.next(a)),k(()=>i.complete()),l(a=>I({ref:e},a)))}function ti({document$:e,tablet$:t}){e.pipe(v(()=>z(".md-toggle--indeterminate")),w(r=>{r.indeterminate=!0,r.checked=!1}),se(r=>h(r,"change").pipe($r(()=>r.classList.contains("md-toggle--indeterminate")),l(()=>r))),oe(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Wa(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function ri({document$:e}){e.pipe(v(()=>z("[data-md-scrollfix]")),w(t=>t.removeAttribute("data-md-scrollfix")),L(Wa),se(t=>h(t,"touchstart").pipe(l(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function oi({viewport$:e,tablet$:t}){Q([We("search"),t]).pipe(l(([r,o])=>r&&!o),v(r=>H(r).pipe(ze(r?400:100))),oe(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function Ua(){return location.protocol==="file:"?ht(`${new URL("search/search_index.js",Qr.base)}`).pipe(l(()=>__index),B(1)):Ue(new URL("search/search_index.json",Qr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var nt=Wo(),At=Qo(),gt=Bo(At),Yr=Ko(),Se=rn(),lr=Fr("(min-width: 960px)"),ii=Fr("(min-width: 1220px)"),ai=Go(),Qr=ue(),si=document.forms.namedItem("search")?Ua():Ve,Br=new x;Rn({alert$:Br});te("navigation.instant")&&In({location$:At,viewport$:Se}).subscribe(nt);var ni;((ni=Qr.version)==null?void 0:ni.provider)==="mike"&&Nn({document$:nt});M(At,gt).pipe(ze(125)).subscribe(()=>{Ke("drawer",!1),Ke("search",!1)});Yr.pipe(L(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ce("link[rel=prev]");typeof t!="undefined"&&ot(t);break;case"n":case".":let r=ce("link[rel=next]");typeof r!="undefined"&&ot(r);break;case"Enter":let o=Re();o instanceof HTMLLabelElement&&o.click()}});ti({document$:nt,tablet$:lr});ri({document$:nt});oi({viewport$:Se,tablet$:lr});var Xe=An(ye("header"),{viewport$:Se}),_t=nt.pipe(l(()=>ye("main")),v(e=>Hn(e,{viewport$:Se,header$:Xe})),B(1)),Na=M(...ne("consent").map(e=>an(e,{target$:gt})),...ne("dialog").map(e=>_n(e,{alert$:Br})),...ne("header").map(e=>Cn(e,{viewport$:Se,header$:Xe,main$:_t})),...ne("palette").map(e=>$n(e)),...ne("search").map(e=>Kn(e,{index$:si,keyboard$:Yr})),...ne("source").map(e=>Jn(e))),Da=$(()=>M(...ne("announce").map(e=>nn(e)),...ne("content").map(e=>Ln(e,{viewport$:Se,target$:gt,print$:ai})),...ne("content").map(e=>te("search.highlight")?Qn(e,{index$:si,location$:At}):T),...ne("header-title").map(e=>kn(e,{viewport$:Se,header$:Xe})),...ne("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?jr(ii,()=>Kr(e,{viewport$:Se,header$:Xe,main$:_t})):jr(lr,()=>Kr(e,{viewport$:Se,header$:Xe,main$:_t}))),...ne("tabs").map(e=>Xn(e,{viewport$:Se,header$:Xe})),...ne("toc").map(e=>Zn(e,{viewport$:Se,header$:Xe,main$:_t,target$:gt})),...ne("top").map(e=>ei(e,{viewport$:Se,header$:Xe,main$:_t,target$:gt})))),ci=nt.pipe(v(()=>Da),qe(Na),B(1));ci.subscribe();window.document$=nt;window.location$=At;window.target$=gt;window.keyboard$=Yr;window.viewport$=Se;window.tablet$=lr;window.screen$=ii;window.print$=ai;window.alert$=Br;window.component$=ci;})(); +//# sourceMappingURL=bundle.dff1b7c8.min.js.map + diff --git a/assets/javascripts/bundle.dff1b7c8.min.js.map b/assets/javascripts/bundle.dff1b7c8.min.js.map new file mode 100644 index 0000000000..82d902384b --- /dev/null +++ b/assets/javascripts/bundle.dff1b7c8.min.js.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/tslib.es6.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/assets/javascripts/browser/document/index.ts", "src/assets/javascripts/browser/element/_/index.ts", "src/assets/javascripts/browser/element/focus/index.ts", "src/assets/javascripts/browser/element/offset/_/index.ts", "src/assets/javascripts/browser/element/offset/content/index.ts", "src/assets/javascripts/utilities/h/index.ts", "src/assets/javascripts/utilities/round/index.ts", "src/assets/javascripts/browser/script/index.ts", "src/assets/javascripts/browser/element/size/_/index.ts", "src/assets/javascripts/browser/element/size/content/index.ts", "src/assets/javascripts/browser/element/visibility/index.ts", "src/assets/javascripts/browser/toggle/index.ts", "src/assets/javascripts/browser/keyboard/index.ts", "src/assets/javascripts/browser/location/_/index.ts", "src/assets/javascripts/browser/location/hash/index.ts", "src/assets/javascripts/browser/media/index.ts", "src/assets/javascripts/browser/request/index.ts", "src/assets/javascripts/browser/viewport/offset/index.ts", "src/assets/javascripts/browser/viewport/size/index.ts", "src/assets/javascripts/browser/viewport/_/index.ts", "src/assets/javascripts/browser/viewport/at/index.ts", "src/assets/javascripts/browser/worker/index.ts", "src/assets/javascripts/_/index.ts", "src/assets/javascripts/components/_/index.ts", "src/assets/javascripts/components/announce/index.ts", "src/assets/javascripts/components/consent/index.ts", "src/assets/javascripts/components/content/annotation/_/index.ts", "src/assets/javascripts/templates/tooltip/index.tsx", "src/assets/javascripts/templates/annotation/index.tsx", "src/assets/javascripts/templates/clipboard/index.tsx", "src/assets/javascripts/templates/search/index.tsx", "src/assets/javascripts/templates/source/index.tsx", "src/assets/javascripts/templates/tabbed/index.tsx", "src/assets/javascripts/templates/table/index.tsx", "src/assets/javascripts/templates/version/index.tsx", "src/assets/javascripts/components/content/annotation/list/index.ts", "src/assets/javascripts/components/content/annotation/block/index.ts", "src/assets/javascripts/components/content/code/_/index.ts", "src/assets/javascripts/components/content/details/index.ts", "src/assets/javascripts/components/content/mermaid/index.css", "src/assets/javascripts/components/content/mermaid/index.ts", "src/assets/javascripts/components/content/table/index.ts", "src/assets/javascripts/components/content/tabs/index.ts", "src/assets/javascripts/components/content/_/index.ts", "src/assets/javascripts/components/dialog/index.ts", "src/assets/javascripts/components/header/_/index.ts", "src/assets/javascripts/components/header/title/index.ts", "src/assets/javascripts/components/main/index.ts", "src/assets/javascripts/components/palette/index.ts", "src/assets/javascripts/integrations/clipboard/index.ts", "src/assets/javascripts/integrations/sitemap/index.ts", "src/assets/javascripts/integrations/instant/index.ts", "src/assets/javascripts/integrations/search/highlighter/index.ts", "src/assets/javascripts/integrations/search/worker/message/index.ts", "src/assets/javascripts/integrations/search/worker/_/index.ts", "src/assets/javascripts/integrations/version/index.ts", "src/assets/javascripts/components/search/query/index.ts", "src/assets/javascripts/components/search/result/index.ts", "src/assets/javascripts/components/search/share/index.ts", "src/assets/javascripts/components/search/suggest/index.ts", "src/assets/javascripts/components/search/_/index.ts", "src/assets/javascripts/components/search/highlight/index.ts", "src/assets/javascripts/components/sidebar/index.ts", "src/assets/javascripts/components/source/facts/github/index.ts", "src/assets/javascripts/components/source/facts/gitlab/index.ts", "src/assets/javascripts/components/source/facts/_/index.ts", "src/assets/javascripts/components/source/_/index.ts", "src/assets/javascripts/components/tabs/index.ts", "src/assets/javascripts/components/toc/index.ts", "src/assets/javascripts/components/top/index.ts", "src/assets/javascripts/patches/indeterminate/index.ts", "src/assets/javascripts/patches/scrollfix/index.ts", "src/assets/javascripts/patches/scrolllock/index.ts", "src/assets/javascripts/polyfills/index.ts"], + "sourceRoot": "../../..", + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2023 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantLoading,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up instant loading, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantLoading({ location$, viewport$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.component$ = component$ /* Component observable */\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
      \n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an