Skip to content

Berry Scripting Language~

This feature is experimental, ESP32 only and included in all ESP32 pre-compiled builds

If you compile your own version, make sure the following is defined:

#define USE_BERRY

Berry logo

See full examples in the Berry Cookbook

Introduction to Berry~

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

Berry Github

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

  • simple scripting and advanced Rules
  • advanced rules, beyond what is possible with native rules
  • advanced automation

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 (coming soon)

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 syntax, support 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 Tutorial~

Make sure you compile Tasmota32 with #define USE_BERRY.

You should see similare lines in the Tasmota logs:

00:00:00.098 BRY: Berry initialized, RAM used=10002
00:00:00.264 BRY: No '' file

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

Berry console

Getting familiar with the REPL~

Try typing simple commands in the REPL. Since the input can be multi-lines, press 'Enter' twice to run the code. Use Up/Down arrows to navigate through history of previous commands.

> 1+1
> 2.0/3
> 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, but it will not display in the Berry Console.

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

Meanwhile the Tasmota log shows:

> tasmota.cmd("Dimmer 60")
The light is bright
The rule function have the general form below where parameters are optionals:

def function_name(value, trigger, msg)
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 string of the message that triggered the rule. If it is a JSON, it has to be explicitly converted to a map object with json.load(msg).


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

> tasmota.cmd("Dimmer 60")
The light is bright

The same fucntion can be used with multiple triggers.

Example 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)
  print("value of adc",adc," is ",value)


Another way to address the same using anonymous functions created dynamically

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

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 a Berry functions.


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()

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]

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 represnting the state of each relays and light (light comes last).

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

Example (2 relays and 1 light):

> tasmota.get_power()
[false, true, false]

> tasmota.set_power(0, 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 mireds, 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, channles, 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]}

  # chaning 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]}

Loading code from filesystem~

You can upload Berry code in the filesytem and load them at runtime. Just be careful to use the *.be extensions.

To load a Berry file, use the load(filename) function. It takes a filename and must end by .be or .bec.

Note: 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.

Currently the precompiled is not loaded unless you explicitly use load("filename.bec") extension, this may change in the future.


Below are the Tasmota specific functions and modules implemented on top of Berry.

Extensions to native Berry~

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.


> log("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.

Note: 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]) -> nil
Equivalent of publish command, publishes a MQTT message on topic with payload. Optional retain parameter.
tasmota.publish_result (payload:string, subtopic:bool) -> 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.cmd (command:string) -> string
Sends any command to Tasmota, like it was type in the console. It returns the result of the command if any.
tasmota.memory () -> map
Returns memory stats similar to the Information page.
Example: {'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.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 (map) -> map
Decompose a time value (in seconds) to its components
Example: tasmota.time_dump(1619560407) -> {'weekday': 2, 'sec': 27, 'month': 4, 'year': 2021, 'day': 27, 'min': 53, 'hour': 21}
tasmota.time_str (map) -> string
Converts a time value (in seconds) to an ISO 8601 string
Example: tasmota.time_str(1619560407) -> 2021-04-27T21:53:27
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_rule (pattern:string, f:function) ->nil
Adds a rule to the rule engine. See above for rule patterns.
tasmota.remove_rule (pattern:string) ->nil
Removes a rule to the rule engine. Silently ignores the pattern if no rule matches.
tasmota.gc () -> int
Triggers a garbage collaction of Berry objects and returns the bytes currently allocated. This is for debug only and shouldn't be normally used. GC is otherwise automatically triggeredd when necessary.

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.

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_str_done (message:string) -> nil
Sets the output for the command to "Done" (localized message).
tasmota.resp_cmnd_str_error (message:string) -> nil
Sets the output for the command to "Error" (localized message).
tasmota.resp_cmnd_str_fail (message:string) -> 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.

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.
tasmota.web_send (message:string) -> nil
Adds an HTML fragment to the Web output.
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.

See examples in the Berry-Cookbook

Functions to manage Relay/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.
> tasmota.get_power()
tasmota.get_light deprecated use light.get
tasmota.set_light deprecated use light.set

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).
> 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.
> 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 physicla 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 PGIO 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 [,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.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

Here are the 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.VL53L0X_XSHUT1, gpio.MAX7219CLK, gpio.MAX7219DIN, gpio.MAX7219CS, gpio.TFMINIPLUS_TX, gpio.TFMINIPLUS_RX, gpio.ZEROCROSS, gpio.HALLEFFECT, gpio.EPD_DATA, gpio.INPUT, gpio.SENSOR_END

wire object~

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()

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

Example with 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.
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 succesful.`
write_bytes (addr:int, reg:int, val:bytes) -> nil
Writes the val bytes sequence as bytes() to address addr register reg.

The following are 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~

This module is a simplified version of os.path module of standard Berry, but disabled on Tasmota because we don't have a full OS.

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 /

introspect module~

This modules 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")

> introspect.set(ins, "a", "bar")

> ins.a
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.vcall (function, [args,]* [list]?) -> any
Calls a function with a dynamically built list of arguments. If the last argument is a list, it is expanded into individual arguments.

webclient class~

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


  • 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 mbetTLS)
  • HTTPS (TLS) only supports cipher ECDHE_RSA_WITH_AES_128_GCM_SHA256 which is both secure and widely supported
  • Support for URL redirections (tbc)
  • 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)

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):

  • Only supports text responses (html, json...) but not binary content yet (no NULL char allowed)
  • 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 access to response headers
  • No support for compressed response


> cl = webclient()
> cl.begin("")
<instance: webclient()>

> r = cl.GET()
> print(r)

> s = cl.get_string()
> print(s)
 <b></b>Alternative firmware for ESP32 based devices with web UI,

Main functions:

WebClient Function 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.
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:string
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.
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.
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.

Request customization:

WebClient Function 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.

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.


# 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 =   # 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.


Back to top