Berry split tasmota-specific reference documentation (#23687)

This commit is contained in:
s-hadinger 2025-07-15 22:17:11 +02:00 committed by GitHub
parent 08f2826e97
commit 722db4157d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 719 additions and 710 deletions

View File

@ -1,12 +1,12 @@
# Berry Language Reference for Tasmota
Note: this file is supposed to use as a reference manual for Generative AI in a compact form. For Claude AI it costs ~10k tokens.
Note: this file is supposed to use as a reference manual for Generative AI in a compact form. For Claude AI it costs ~6k tokens.
## Introduction
Berry is an ultra-lightweight, dynamically typed embedded scripting language designed for resource-constrained environments. The language primarily supports procedural programming, with additional support for object-oriented and functional programming paradigms. Berry's key design goal is to run efficiently on embedded devices with very limited memory, making the language highly streamlined while maintaining rich scripting capabilities.
**Tasmota Integration**: Berry is the next generation scripting for Tasmota, embedded by default in all ESP32 based firmwares (NOT supported on ESP8266). It is used for advanced scripting, superseding Rules, and enables building drivers, automations, and UI extensions.
**Tasmota Integration**: Berry is integrated into Tasmota firmware. For Tasmota-specific features, see the companion document `BERRY_TASMOTA.md`.
## Basic Information
@ -972,711 +972,3 @@ assert(condition, "message") # Raises with custom message
- Avoid creating unnecessary objects
- Reuse buffers when processing large data
- Use native functions for performance-critical code
## Tasmota-Specific Features
### Tasmota-Specific Modules
Beyond standard Berry modules, Tasmota provides additional modules:
#### `tasmota` - Core integration module (automatically imported)
#### `light` - Light control (automatically imported)
#### `mqtt` - MQTT operations (`import mqtt`)
#### `webserver` - Web server extensions (`import webserver`)
#### `gpio` - GPIO control (`import gpio`)
#### `persist` - Data persistence (`import persist`)
#### `path` - File system operations (`import path`)
#### `energy` - Energy monitoring (automatically imported)
#### `display` - Display driver integration (`import display`)
#### `crypto` - Cryptographic functions (`import crypto`)
#### `re` - Regular expressions (`import re`)
#### `mdns` - mDNS/Bonjour support (`import mdns`)
#### `ULP` - Ultra Low Power coprocessor (`import ULP`)
#### `uuid` - UUID generation (`import uuid`)
#### `crc` - CRC calculations (`import crc`)
### Tasmota Constants and Enums
```berry
# GPIO constants (gpio module)
gpio.INPUT, gpio.OUTPUT, gpio.PULLUP, gpio.PULLDOWN
gpio.HIGH, gpio.LOW
gpio.REL1, gpio.KEY1, gpio.LED1, gpio.I2C_SCL, gpio.I2C_SDA
# ... many more GPIO function constants
# Serial constants
serial.SERIAL_8N1, serial.SERIAL_7E1, etc.
# Webserver constants
webserver.HTTP_GET, webserver.HTTP_POST, webserver.HTTP_OPTIONS, webserver.HTTP_ANY
webserver.HTTP_OFF, webserver.HTTP_USER, webserver.HTTP_ADMIN, webserver.HTTP_MANAGER
webserver.HTTP_MANAGER_RESET_ONLY
webserver.BUTTON_MAIN, webserver.BUTTON_CONFIGURATION, webserver.BUTTON_INFORMATION
webserver.BUTTON_MANAGEMENT, webserver.BUTTON_MODULE
```
### Console and REPL
Access Berry console via *Configuration**Berry Scripting Console*. The console supports:
- Multi-line input (press Enter twice or click "Run")
- Command history (arrow keys)
- Colorful syntax highlighting
- Berry VM restart with `BrRestart` command
### File System and Loading
Berry files can be source (`.be`) or pre-compiled bytecode (`.bec`):
```berry
load("filename") # Loads .be or .bec file
tasmota.compile("file.be") # Compiles .be to .bec
```
**Autostart**: Place `autoexec.be` in filesystem to run Berry code at boot.
### Tasmota Integration Functions
#### Core Tasmota Functions
```berry
# System information
tasmota.get_free_heap() # Free heap bytes
tasmota.memory() # Memory stats map
tasmota.arch() # Architecture: "esp32", "esp32s2", etc.
tasmota.millis() # Milliseconds since boot
tasmota.yield() # Give time to low-level functions
tasmota.delay(ms) # Block execution for ms milliseconds
# Commands and responses
tasmota.cmd("command") # Execute Tasmota command
tasmota.resp_cmnd_done() # Respond "Done"
tasmota.resp_cmnd_error() # Respond "Error"
tasmota.resp_cmnd_str(msg) # Custom response string
tasmota.resp_cmnd(json) # Custom JSON response
# Configuration
tasmota.get_option(index) # Get SetOption value
tasmota.read_sensors() # Get sensor JSON string
tasmota.wifi() # WiFi connection info
tasmota.eth() # Ethernet connection info
```
#### Rules and Events
```berry
# Add rules (similar to Tasmota Rules but more powerful)
tasmota.add_rule("trigger", function)
tasmota.add_rule(["trigger1", "trigger2"], function) # AND logic
tasmota.remove_rule("trigger")
# Rule function signature
def rule_function(value, trigger, msg)
# value: trigger value (%value% equivalent)
# trigger: full trigger string
# msg: parsed JSON map or original string
end
# Examples
tasmota.add_rule("Dimmer>50", def() print("Bright!") end)
tasmota.add_rule("ANALOG#A1>300", def(val) print("ADC:", val) end)
```
#### Timers and Scheduling
```berry
# Timers (50ms resolution)
tasmota.set_timer(delay_ms, function)
tasmota.remove_timer(id)
tasmota.defer(function) # Run in next millisecond
# Cron scheduling
tasmota.add_cron("*/15 * * * * *", function, "id")
tasmota.remove_cron("id")
tasmota.next_cron("id") # Next execution timestamp
# Time functions
tasmota.rtc() # Current time info
tasmota.time_dump(timestamp) # Decompose timestamp
tasmota.time_str(timestamp) # ISO 8601 string
tasmota.strftime(format, timestamp)
tasmota.strptime(time_str, format)
```
#### Device Control
```berry
# Relays and Power
tasmota.get_power() # Array of relay states
tasmota.set_power(idx, state) # Set relay state
# Lights (use light module)
light.get() # Current light status
light.set({"power": true, "bri": 128, "hue": 120})
# Light attributes: power, bri (0-255), hue (0-360), sat (0-255),
# ct (153-500), rgb (hex string), channels (array)
```
#### Custom Commands
```berry
# Add custom Tasmota commands
def my_command(cmd, idx, payload, payload_json)
# cmd: command name, idx: command index
# payload: raw string, payload_json: parsed JSON
tasmota.resp_cmnd_done()
end
tasmota.add_cmd("MyCmd", my_command)
tasmota.remove_cmd("MyCmd")
```
### Tasmota Drivers
Create complete Tasmota drivers by implementing event methods:
```berry
class MyDriver
def every_second() # Called every second
end
def every_50ms() # Called every 50ms
end
def web_sensor() # Add to web UI
tasmota.web_send("{s}Sensor{m}Value{e}")
end
def json_append() # Add to JSON teleperiod
tasmota.response_append(',"MySensor":{"Value":123}')
end
def web_add_main_button() # Add button to main page
import webserver
webserver.content_send("<button onclick='la(\"&myaction=1\");'>My Button</button>")
end
def button_pressed() # Handle button press
end
def mqtt_data(topic, idx, data, databytes) # Handle MQTT
end
def save_before_restart() # Before restart
end
end
# Register driver
driver = MyDriver()
tasmota.add_driver(driver)
```
### Fast Loop
For near real-time events (200Hz, 5ms intervals):
```berry
def fast_function()
# High-frequency processing
end
tasmota.add_fast_loop(fast_function)
tasmota.remove_fast_loop(fast_function)
```
### GPIO Control
```berry
import gpio
# GPIO detection and control
gpio.pin_used(gpio.REL1) # Check if GPIO is used
gpio.pin(gpio.REL1) # Get physical GPIO number
gpio.digital_write(pin, gpio.HIGH) # Set GPIO state
gpio.digital_read(pin) # Read GPIO state
gpio.pin_mode(pin, gpio.OUTPUT) # Set GPIO mode
# PWM control
gpio.set_pwm(pin, duty, phase) # Set PWM value
gpio.set_pwm_freq(pin, freq) # Set PWM frequency
# DAC (ESP32 GPIO 25-26, ESP32-S2 GPIO 17-18)
gpio.dac_voltage(pin, voltage_mv) # Set DAC voltage
# Counters
gpio.counter_read(counter) # Read counter value
gpio.counter_set(counter, value) # Set counter value
```
### I²C Communication
```berry
# Use wire1 or wire2 for I²C buses
wire1.scan() # Scan for devices
wire1.detect(addr) # Check if device present
wire1.read(addr, reg, size) # Read from device
wire1.write(addr, reg, val, size) # Write to device
wire1.read_bytes(addr, reg, size) # Read as bytes
wire1.write_bytes(addr, reg, bytes) # Write bytes
# Find device on any bus
wire = tasmota.wire_scan(addr, i2c_index)
```
### MQTT Integration
```berry
import mqtt
# MQTT operations
mqtt.publish(topic, payload, retain)
mqtt.subscribe(topic, function) # Subscribe with callback
mqtt.unsubscribe(topic)
mqtt.connected() # Check connection status
# Callback function signature
def mqtt_callback(topic, idx, payload_s, payload_b)
# topic: full topic, payload_s: string, payload_b: bytes
return true # Return true if handled
end
```
### Web Server Extensions
```berry
import webserver
# In driver's web_add_handler() method
webserver.on("/my_page", def()
webserver.content_send("<html>My Page</html>")
end)
# Request handling
webserver.has_arg("param") # Check parameter exists
webserver.arg("param") # Get parameter value
webserver.arg_size() # Number of parameters
# Response functions
webserver.content_send(html) # Send HTML content
webserver.content_button() # Standard button
webserver.html_escape(str) # Escape HTML
```
### Persistence
```berry
import persist
# Automatic persistence to _persist.json
persist.my_value = 123
persist.save() # Force save to flash
persist.has("key") # Check if key exists
persist.remove("key") # Remove key
persist.find("key", default) # Get with default
```
### Network Clients
#### HTTP/HTTPS Client
```berry
cl = webclient()
cl.begin("https://example.com/api")
cl.set_auth("user", "pass")
cl.add_header("Content-Type", "application/json")
result = cl.GET() # or POST(payload)
if result == 200
response = cl.get_string()
# or cl.write_file("filename") for binary
end
cl.close()
```
#### TCP Client
```berry
tcp = tcpclient()
tcp.connect("192.168.1.100", 80)
tcp.write("GET / HTTP/1.0\r\n\r\n")
response = tcp.read()
tcp.close()
```
#### UDP Communication
```berry
u = udp()
u.begin("", 2000) # Listen on port 2000
u.send("192.168.1.10", 2000, bytes("Hello"))
# Receive (polling)
packet = u.read() # Returns bytes or nil
if packet
print("From:", u.remote_ip, u.remote_port)
end
```
### Serial Communication
```berry
ser = serial(rx_gpio, tx_gpio, baud, serial.SERIAL_8N1)
ser.write(bytes("Hello")) # Send data
data = ser.read() # Read available data
ser.available() # Check bytes available
ser.flush() # Flush buffers
ser.close() # Close port
```
### Cryptography
```berry
import crypto
# AES encryption
aes = crypto.AES_GCM(key_32_bytes, iv_12_bytes)
encrypted = aes.encrypt(plaintext)
tag = aes.tag()
# Hashing
crypto.SHA256().update(data).finish() # SHA256 hash
crypto.MD5().update(data).finish() # MD5 hash
# HMAC
crypto.HMAC_SHA256(key).update(data).finish()
```
### File System Operations
```berry
import path
path.exists("filename") # Check file exists
path.listdir("/") # List directory
path.remove("filename") # Delete file
path.mkdir("dirname") # Create directory
path.last_modified("file") # File timestamp
```
### Regular Expressions
```berry
import re
# Pattern matching
matches = re.search("a.*?b(z+)", "aaaabbbzzz") # Returns matches array
all_matches = re.searchall('<([a-zA-Z]+)>', html) # All matches
parts = re.split('/', "path/to/file") # Split string
# Compiled patterns (faster for reuse)
pattern = re.compilebytes("\\d+")
matches = re.search(pattern, "abc123def")
```
### Energy Monitoring
```berry
# Read energy values
energy.voltage # Main phase voltage
energy.current # Main phase current
energy.active_power # Active power (W)
energy.total # Total energy (kWh)
# Multi-phase access
energy.voltage_phases[0] # Phase 0 voltage
energy.current_phases[1] # Phase 1 current
# Berry energy driver (with OPTION_A 9 GPIO)
if energy.driver_enabled()
energy.voltage = 240
energy.current = 1.5
energy.active_power = 360 # This drives energy calculation
end
```
### Display Integration
```berry
import display
# Initialize display driver
display.start(display_ini_string)
display.started() # Check if initialized
display.dimmer(50) # Set brightness 0-100
display.driver_name() # Get driver name
# Touch screen updates
display.touch_update(touches, x, y, gesture)
```
### Advanced Features
#### ULP (Ultra Low Power) Coprocessor
```berry
import ULP
ULP.wake_period(0, 500000) # Configure wake timer
ULP.load(bytecode) # Load ULP program
ULP.run() # Execute ULP program
ULP.set_mem(addr, value) # Set RTC memory
ULP.get_mem(addr) # Get RTC memory
```
#### mDNS Support
```berry
import mdns
mdns.start("hostname") # Start mDNS
mdns.add_service("_http", "_tcp", 80, {"path": "/"})
mdns.stop() # Stop mDNS
```
### Error Handling Patterns
Many Tasmota functions return `nil` for errors rather than raising exceptions:
```berry
# Check return values
data = json.load(json_string)
if data == nil
print("Invalid JSON")
end
# Wire operations
result = wire1.read(addr, reg, 1)
if result == nil
print("I2C read failed")
end
```
### Best Practices for Tasmota
1. **Memory Management**: Use `tasmota.gc()` to monitor memory usage
2. **Non-blocking**: Use timers instead of `delay()` for long waits
3. **Error Handling**: Always check return values for `nil`
4. **Persistence**: Use `persist` module for settings that survive reboots
5. **Performance**: Use fast_loop sparingly, prefer regular driver events
6. **Debugging**: Enable `#define USE_BERRY_DEBUG` for development
### Tasmota Extensions to Standard Modules
#### `bytes` class extensions
```berry
b = bytes("1122AA") # From hex string
b = bytes(-8) # Fixed size buffer
b.tohex() # To hex string
b.tob64() # To base64
b.fromhex("AABBCC") # Load from hex
b.fromb64("SGVsbG8=") # Load from base64
b.asstring() # To raw string
```
## Common Tasmota Berry Patterns
### Simple Sensor Driver
```berry
class MySensor
var wire, addr
def init()
self.addr = 0x48
self.wire = tasmota.wire_scan(self.addr, 99) # I2C index 99
if self.wire
print("MySensor found on bus", self.wire.bus)
end
end
def every_second()
if !self.wire return end
var temp = self.wire.read(self.addr, 0x00, 2) # Read temperature
self.temperature = temp / 256.0 # Convert to Celsius
end
def web_sensor()
if !self.wire return end
import string
var msg = string.format("{s}MySensor Temp{m}%.1f °C{e}", self.temperature)
tasmota.web_send_decimal(msg)
end
def json_append()
if !self.wire return end
import string
var msg = string.format(',"MySensor":{"Temperature":%.1f}', self.temperature)
tasmota.response_append(msg)
end
end
sensor = MySensor()
tasmota.add_driver(sensor)
```
### Custom Command with JSON Response
```berry
def my_status_cmd(cmd, idx, payload, payload_json)
import string
var response = {
"Uptime": tasmota.millis(),
"FreeHeap": tasmota.get_free_heap(),
"WiFi": tasmota.wifi("rssi")
}
tasmota.resp_cmnd(json.dump(response))
end
tasmota.add_cmd("MyStatus", my_status_cmd)
```
### MQTT Automation
```berry
import mqtt
def handle_sensor_data(topic, idx, payload_s, payload_b)
var data = json.load(payload_s)
if data && data.find("temperature")
var temp = data["temperature"]
if temp > 25
tasmota.cmd("Power1 ON") # Turn on fan
elif temp < 20
tasmota.cmd("Power1 OFF") # Turn off fan
end
end
return true
end
mqtt.subscribe("sensors/+/temperature", handle_sensor_data)
```
### Web UI Button with Action
```berry
class WebButton
def web_add_main_button()
import webserver
webserver.content_send("<p><button onclick='la(\"&toggle_led=1\");'>Toggle LED</button></p>")
end
def web_sensor()
import webserver
if webserver.has_arg("toggle_led")
# Toggle GPIO2 (built-in LED on many ESP32 boards)
var pin = 2
var current = gpio.digital_read(pin)
gpio.digital_write(pin, !current)
print("LED toggled to", !current)
end
end
end
button = WebButton()
tasmota.add_driver(button)
```
### Scheduled Task with Persistence
```berry
import persist
class ScheduledTask
def init()
if !persist.has("task_count")
persist.task_count = 0
end
# Run every 5 minutes
tasmota.add_cron("0 */5 * * * *", /-> self.run_task(), "my_task")
end
def run_task()
persist.task_count += 1
print("Task executed", persist.task_count, "times")
# Do something useful
var sensors = tasmota.read_sensors()
print("Current sensors:", sensors)
persist.save() # Save counter to flash
end
end
task = ScheduledTask()
```
### HTTP API Client
```berry
class WeatherAPI
var api_key, city
def init(key, city_name)
self.api_key = key
self.city = city_name
tasmota.add_cron("0 0 * * * *", /-> self.fetch_weather(), "weather")
end
def fetch_weather()
var cl = webclient()
var url = f"http://api.openweathermap.org/data/2.5/weather?q={self.city}&appid={self.api_key}"
cl.begin(url)
var result = cl.GET()
if result == 200
var response = cl.get_string()
var data = json.load(response)
if data
var temp = data["main"]["temp"] - 273.15 # Kelvin to Celsius
print(f"Weather in {self.city}: {temp:.1f}°C")
# Store in global for other scripts to use
import global
global.weather_temp = temp
end
end
cl.close()
end
end
# weather = WeatherAPI("your_api_key", "London")
```
### Rule-based Automation
```berry
# Advanced rule that combines multiple conditions
tasmota.add_rule(["ANALOG#A0>500", "Switch1#State=1"],
def(values, triggers)
print("Both conditions met:")
print("ADC value:", values[0])
print("Switch state:", values[1])
tasmota.cmd("Power2 ON") # Activate something
end
)
# Time-based rule
tasmota.add_rule("Time#Minute=30",
def()
if tasmota.rtc()["hour"] == 18 # 6:30 PM
tasmota.cmd("Dimmer 20") # Dim lights for evening
end
end
)
```
## Best Practices and Tips
1. **Always check for nil returns** from Tasmota functions
2. **Use timers instead of delay()** to avoid blocking Tasmota
3. **Implement proper error handling** in I²C and network operations
4. **Use persist module** for settings that should survive reboots
5. **Test memory usage** with `tasmota.gc()` during development
6. **Use fast_loop sparingly** - it runs 200 times per second
7. **Prefer driver events** over polling when possible
8. **Use f-strings** for readable string formatting
9. **Import modules only when needed** to save memory
10. **Use `tasmota.wire_scan()`** instead of manual I²C bus detection

View File

@ -0,0 +1,717 @@
# Berry for Tasmota
This document covers Tasmota-specific Berry features and extensions, complementing the general Berry language reference.
## Introduction
Berry is the next generation scripting language for Tasmota, embedded by default in all ESP32 based firmwares (NOT supported on ESP8266). It is used for advanced scripting, superseding Rules, and enables building drivers, automations, and UI extensions.
## Tasmota-Specific Modules
Beyond standard Berry modules, Tasmota provides additional modules:
| Module | Description | Import |
|--------|-------------|--------|
| `tasmota` | Core integration module | Automatically imported |
| `light` | Light control | Automatically imported |
| `mqtt` | MQTT operations | `import mqtt` |
| `webserver` | Web server extensions | `import webserver` |
| `gpio` | GPIO control | `import gpio` |
| `persist` | Data persistence | `import persist` |
| `path` | File system operations | `import path` |
| `energy` | Energy monitoring | Automatically imported |
| `display` | Display driver integration | `import display` |
| `crypto` | Cryptographic functions | `import crypto` |
| `re` | Regular expressions | `import re` |
| `mdns` | mDNS/Bonjour support | `import mdns` |
| `ULP` | Ultra Low Power coprocessor | `import ULP` |
| `uuid` | UUID generation | `import uuid` |
| `crc` | CRC calculations | `import crc` |
## Additional Resources
For Tasmota-specific Berry features and extensions, please refer to the companion document `BERRY_TASMOTA.md`.
### Tasmota Constants and Enums
```berry
# GPIO constants (gpio module)
gpio.INPUT, gpio.OUTPUT, gpio.PULLUP, gpio.PULLDOWN
gpio.HIGH, gpio.LOW
gpio.REL1, gpio.KEY1, gpio.LED1, gpio.I2C_SCL, gpio.I2C_SDA
# ... many more GPIO function constants
# Serial constants
serial.SERIAL_8N1, serial.SERIAL_7E1, etc.
# Webserver constants
webserver.HTTP_GET, webserver.HTTP_POST, webserver.HTTP_OPTIONS, webserver.HTTP_ANY
webserver.HTTP_OFF, webserver.HTTP_USER, webserver.HTTP_ADMIN, webserver.HTTP_MANAGER
webserver.HTTP_MANAGER_RESET_ONLY
webserver.BUTTON_MAIN, webserver.BUTTON_CONFIGURATION, webserver.BUTTON_INFORMATION
webserver.BUTTON_MANAGEMENT, webserver.BUTTON_MODULE
```
### Console and REPL
Access Berry console via *Configuration**Berry Scripting Console*. The console supports:
- Multi-line input (press Enter twice or click "Run")
- Command history (arrow keys)
- Colorful syntax highlighting
- Berry VM restart with `BrRestart` command
### File System and Loading
Berry files can be source (`.be`) or pre-compiled bytecode (`.bec`):
```berry
load("filename") # Loads .be or .bec file
tasmota.compile("file.be") # Compiles .be to .bec
```
**Autostart**: Place `autoexec.be` in filesystem to run Berry code at boot.
### Tasmota Integration Functions
#### Core Tasmota Functions
```berry
# System information
tasmota.get_free_heap() # Free heap bytes
tasmota.memory() # Memory stats map
tasmota.arch() # Architecture: "esp32", "esp32s2", etc.
tasmota.millis() # Milliseconds since boot
tasmota.yield() # Give time to low-level functions
tasmota.delay(ms) # Block execution for ms milliseconds
# Commands and responses
tasmota.cmd("command") # Execute Tasmota command
tasmota.resp_cmnd_done() # Respond "Done"
tasmota.resp_cmnd_error() # Respond "Error"
tasmota.resp_cmnd_str(msg) # Custom response string
tasmota.resp_cmnd(json) # Custom JSON response
# Configuration
tasmota.get_option(index) # Get SetOption value
tasmota.read_sensors() # Get sensor JSON string
tasmota.wifi() # WiFi connection info
tasmota.eth() # Ethernet connection info
```
#### Rules and Events
```berry
# Add rules (similar to Tasmota Rules but more powerful)
tasmota.add_rule("trigger", function)
tasmota.add_rule(["trigger1", "trigger2"], function) # AND logic
tasmota.remove_rule("trigger")
# Rule function signature
def rule_function(value, trigger, msg)
# value: trigger value (%value% equivalent)
# trigger: full trigger string
# msg: parsed JSON map or original string
end
# Examples
tasmota.add_rule("Dimmer>50", def() print("Bright!") end)
tasmota.add_rule("ANALOG#A1>300", def(val) print("ADC:", val) end)
```
#### Timers and Scheduling
```berry
# Timers (50ms resolution)
tasmota.set_timer(delay_ms, function)
tasmota.remove_timer(id)
tasmota.defer(function) # Run in next millisecond
# Cron scheduling
tasmota.add_cron("*/15 * * * * *", function, "id")
tasmota.remove_cron("id")
tasmota.next_cron("id") # Next execution timestamp
# Time functions
tasmota.rtc() # Current time info
tasmota.time_dump(timestamp) # Decompose timestamp
tasmota.time_str(timestamp) # ISO 8601 string
tasmota.strftime(format, timestamp)
tasmota.strptime(time_str, format)
```
#### Device Control
```berry
# Relays and Power
tasmota.get_power() # Array of relay states
tasmota.set_power(idx, state) # Set relay state
# Lights (use light module)
light.get() # Current light status
light.set({"power": true, "bri": 128, "hue": 120})
# Light attributes: power, bri (0-255), hue (0-360), sat (0-255),
# ct (153-500), rgb (hex string), channels (array)
```
#### Custom Commands
```berry
# Add custom Tasmota commands
def my_command(cmd, idx, payload, payload_json)
# cmd: command name, idx: command index
# payload: raw string, payload_json: parsed JSON
tasmota.resp_cmnd_done()
end
tasmota.add_cmd("MyCmd", my_command)
tasmota.remove_cmd("MyCmd")
```
### Tasmota Drivers
Create complete Tasmota drivers by implementing event methods:
```berry
class MyDriver
def every_second() # Called every second
end
def every_50ms() # Called every 50ms
end
def web_sensor() # Add to web UI
tasmota.web_send("{s}Sensor{m}Value{e}")
end
def json_append() # Add to JSON teleperiod
tasmota.response_append(',"MySensor":{"Value":123}')
end
def web_add_main_button() # Add button to main page
import webserver
webserver.content_send("<button onclick='la(\"&myaction=1\");'>My Button</button>")
end
def button_pressed() # Handle button press
end
def mqtt_data(topic, idx, data, databytes) # Handle MQTT
end
def save_before_restart() # Before restart
end
end
# Register driver
driver = MyDriver()
tasmota.add_driver(driver)
```
### Fast Loop
For near real-time events (200Hz, 5ms intervals):
```berry
def fast_function()
# High-frequency processing
end
tasmota.add_fast_loop(fast_function)
tasmota.remove_fast_loop(fast_function)
```
### GPIO Control
```berry
import gpio
# GPIO detection and control
gpio.pin_used(gpio.REL1) # Check if GPIO is used
gpio.pin(gpio.REL1) # Get physical GPIO number
gpio.digital_write(pin, gpio.HIGH) # Set GPIO state
gpio.digital_read(pin) # Read GPIO state
gpio.pin_mode(pin, gpio.OUTPUT) # Set GPIO mode
# PWM control
gpio.set_pwm(pin, duty, phase) # Set PWM value
gpio.set_pwm_freq(pin, freq) # Set PWM frequency
# DAC (ESP32 GPIO 25-26, ESP32-S2 GPIO 17-18)
gpio.dac_voltage(pin, voltage_mv) # Set DAC voltage
# Counters
gpio.counter_read(counter) # Read counter value
gpio.counter_set(counter, value) # Set counter value
```
### I²C Communication
```berry
# Use wire1 or wire2 for I²C buses
wire1.scan() # Scan for devices
wire1.detect(addr) # Check if device present
wire1.read(addr, reg, size) # Read from device
wire1.write(addr, reg, val, size) # Write to device
wire1.read_bytes(addr, reg, size) # Read as bytes
wire1.write_bytes(addr, reg, bytes) # Write bytes
# Find device on any bus
wire = tasmota.wire_scan(addr, i2c_index)
```
### MQTT Integration
```berry
import mqtt
# MQTT operations
mqtt.publish(topic, payload, retain)
mqtt.subscribe(topic, function) # Subscribe with callback
mqtt.unsubscribe(topic)
mqtt.connected() # Check connection status
# Callback function signature
def mqtt_callback(topic, idx, payload_s, payload_b)
# topic: full topic, payload_s: string, payload_b: bytes
return true # Return true if handled
end
```
### Web Server Extensions
```berry
import webserver
# In driver's web_add_handler() method
webserver.on("/my_page", def()
webserver.content_send("<html>My Page</html>")
end)
# Request handling
webserver.has_arg("param") # Check parameter exists
webserver.arg("param") # Get parameter value
webserver.arg_size() # Number of parameters
# Response functions
webserver.content_send(html) # Send HTML content
webserver.content_button() # Standard button
webserver.html_escape(str) # Escape HTML
```
### Persistence
```berry
import persist
# Automatic persistence to _persist.json
persist.my_value = 123
persist.save() # Force save to flash
persist.has("key") # Check if key exists
persist.remove("key") # Remove key
persist.find("key", default) # Get with default
```
### Network Clients
#### HTTP/HTTPS Client
```berry
cl = webclient()
cl.begin("https://example.com/api")
cl.set_auth("user", "pass")
cl.add_header("Content-Type", "application/json")
result = cl.GET() # or POST(payload)
if result == 200
response = cl.get_string()
# or cl.write_file("filename") for binary
end
cl.close()
```
#### TCP Client
```berry
tcp = tcpclient()
tcp.connect("192.168.1.100", 80)
tcp.write("GET / HTTP/1.0\r\n\r\n")
response = tcp.read()
tcp.close()
```
#### UDP Communication
```berry
u = udp()
u.begin("", 2000) # Listen on port 2000
u.send("192.168.1.10", 2000, bytes("Hello"))
# Receive (polling)
packet = u.read() # Returns bytes or nil
if packet
print("From:", u.remote_ip, u.remote_port)
end
```
### Serial Communication
```berry
ser = serial(rx_gpio, tx_gpio, baud, serial.SERIAL_8N1)
ser.write(bytes("Hello")) # Send data
data = ser.read() # Read available data
ser.available() # Check bytes available
ser.flush() # Flush buffers
ser.close() # Close port
```
### Cryptography
```berry
import crypto
# AES encryption
aes = crypto.AES_GCM(key_32_bytes, iv_12_bytes)
encrypted = aes.encrypt(plaintext)
tag = aes.tag()
# Hashing
crypto.SHA256().update(data).finish() # SHA256 hash
crypto.MD5().update(data).finish() # MD5 hash
# HMAC
crypto.HMAC_SHA256(key).update(data).finish()
```
### File System Operations
```berry
import path
path.exists("filename") # Check file exists
path.listdir("/") # List directory
path.remove("filename") # Delete file
path.mkdir("dirname") # Create directory
path.last_modified("file") # File timestamp
```
### Regular Expressions
```berry
import re
# Pattern matching
matches = re.search("a.*?b(z+)", "aaaabbbzzz") # Returns matches array
all_matches = re.searchall('<([a-zA-Z]+)>', html) # All matches
parts = re.split('/', "path/to/file") # Split string
# Compiled patterns (faster for reuse)
pattern = re.compilebytes("\\d+")
matches = re.search(pattern, "abc123def")
```
### Energy Monitoring
```berry
# Read energy values
energy.voltage # Main phase voltage
energy.current # Main phase current
energy.active_power # Active power (W)
energy.total # Total energy (kWh)
# Multi-phase access
energy.voltage_phases[0] # Phase 0 voltage
energy.current_phases[1] # Phase 1 current
# Berry energy driver (with OPTION_A 9 GPIO)
if energy.driver_enabled()
energy.voltage = 240
energy.current = 1.5
energy.active_power = 360 # This drives energy calculation
end
```
### Display Integration
```berry
import display
# Initialize display driver
display.start(display_ini_string)
display.started() # Check if initialized
display.dimmer(50) # Set brightness 0-100
display.driver_name() # Get driver name
# Touch screen updates
display.touch_update(touches, x, y, gesture)
```
### Advanced Features
#### ULP (Ultra Low Power) Coprocessor
```berry
import ULP
ULP.wake_period(0, 500000) # Configure wake timer
ULP.load(bytecode) # Load ULP program
ULP.run() # Execute ULP program
ULP.set_mem(addr, value) # Set RTC memory
ULP.get_mem(addr) # Get RTC memory
```
#### mDNS Support
```berry
import mdns
mdns.start("hostname") # Start mDNS
mdns.add_service("_http", "_tcp", 80, {"path": "/"})
mdns.stop() # Stop mDNS
```
### Error Handling Patterns
Many Tasmota functions return `nil` for errors rather than raising exceptions:
```berry
# Check return values
data = json.load(json_string)
if data == nil
print("Invalid JSON")
end
# Wire operations
result = wire1.read(addr, reg, 1)
if result == nil
print("I2C read failed")
end
```
### Best Practices for Tasmota
1. **Memory Management**: Use `tasmota.gc()` to monitor memory usage
2. **Non-blocking**: Use timers instead of `delay()` for long waits
3. **Error Handling**: Always check return values for `nil`
4. **Persistence**: Use `persist` module for settings that survive reboots
5. **Performance**: Use fast_loop sparingly, prefer regular driver events
6. **Debugging**: Enable `#define USE_BERRY_DEBUG` for development
### Tasmota Extensions to Standard Modules
#### `bytes` class extensions
```berry
b = bytes("1122AA") # From hex string
b = bytes(-8) # Fixed size buffer
b.tohex() # To hex string
b.tob64() # To base64
b.fromhex("AABBCC") # Load from hex
b.fromb64("SGVsbG8=") # Load from base64
b.asstring() # To raw string
```
## Common Tasmota Berry Patterns
### Simple Sensor Driver
```berry
class MySensor
var wire, addr
def init()
self.addr = 0x48
self.wire = tasmota.wire_scan(self.addr, 99) # I2C index 99
if self.wire
print("MySensor found on bus", self.wire.bus)
end
end
def every_second()
if !self.wire return end
var temp = self.wire.read(self.addr, 0x00, 2) # Read temperature
self.temperature = temp / 256.0 # Convert to Celsius
end
def web_sensor()
if !self.wire return end
import string
var msg = string.format("{s}MySensor Temp{m}%.1f °C{e}", self.temperature)
tasmota.web_send_decimal(msg)
end
def json_append()
if !self.wire return end
import string
var msg = string.format(',"MySensor":{"Temperature":%.1f}', self.temperature)
tasmota.response_append(msg)
end
end
sensor = MySensor()
tasmota.add_driver(sensor)
```
### Custom Command with JSON Response
```berry
def my_status_cmd(cmd, idx, payload, payload_json)
import string
var response = {
"Uptime": tasmota.millis(),
"FreeHeap": tasmota.get_free_heap(),
"WiFi": tasmota.wifi("rssi")
}
tasmota.resp_cmnd(json.dump(response))
end
tasmota.add_cmd("MyStatus", my_status_cmd)
```
### MQTT Automation
```berry
import mqtt
def handle_sensor_data(topic, idx, payload_s, payload_b)
var data = json.load(payload_s)
if data && data.find("temperature")
var temp = data["temperature"]
if temp > 25
tasmota.cmd("Power1 ON") # Turn on fan
elif temp < 20
tasmota.cmd("Power1 OFF") # Turn off fan
end
end
return true
end
mqtt.subscribe("sensors/+/temperature", handle_sensor_data)
```
### Web UI Button with Action
```berry
class WebButton
def web_add_main_button()
import webserver
webserver.content_send("<p><button onclick='la(\"&toggle_led=1\");'>Toggle LED</button></p>")
end
def web_sensor()
import webserver
if webserver.has_arg("toggle_led")
# Toggle GPIO2 (built-in LED on many ESP32 boards)
var pin = 2
var current = gpio.digital_read(pin)
gpio.digital_write(pin, !current)
print("LED toggled to", !current)
end
end
end
button = WebButton()
tasmota.add_driver(button)
```
### Scheduled Task with Persistence
```berry
import persist
class ScheduledTask
def init()
if !persist.has("task_count")
persist.task_count = 0
end
# Run every 5 minutes
tasmota.add_cron("0 */5 * * * *", /-> self.run_task(), "my_task")
end
def run_task()
persist.task_count += 1
print("Task executed", persist.task_count, "times")
# Do something useful
var sensors = tasmota.read_sensors()
print("Current sensors:", sensors)
persist.save() # Save counter to flash
end
end
task = ScheduledTask()
```
### HTTP API Client
```berry
class WeatherAPI
var api_key, city
def init(key, city_name)
self.api_key = key
self.city = city_name
tasmota.add_cron("0 0 * * * *", /-> self.fetch_weather(), "weather")
end
def fetch_weather()
var cl = webclient()
var url = f"http://api.openweathermap.org/data/2.5/weather?q={self.city}&appid={self.api_key}"
cl.begin(url)
var result = cl.GET()
if result == 200
var response = cl.get_string()
var data = json.load(response)
if data
var temp = data["main"]["temp"] - 273.15 # Kelvin to Celsius
print(f"Weather in {self.city}: {temp:.1f}°C")
# Store in global for other scripts to use
import global
global.weather_temp = temp
end
end
cl.close()
end
end
# weather = WeatherAPI("your_api_key", "London")
```
### Rule-based Automation
```berry
# Advanced rule that combines multiple conditions
tasmota.add_rule(["ANALOG#A0>500", "Switch1#State=1"],
def(values, triggers)
print("Both conditions met:")
print("ADC value:", values[0])
print("Switch state:", values[1])
tasmota.cmd("Power2 ON") # Activate something
end
)
# Time-based rule
tasmota.add_rule("Time#Minute=30",
def()
if tasmota.rtc()["hour"] == 18 # 6:30 PM
tasmota.cmd("Dimmer 20") # Dim lights for evening
end
end
)
```
## Best Practices and Tips
1. **Always check for nil returns** from Tasmota functions
2. **Use timers instead of delay()** to avoid blocking Tasmota
3. **Implement proper error handling** in I²C and network operations
4. **Use persist module** for settings that should survive reboots
5. **Test memory usage** with `tasmota.gc()` during development
6. **Use fast_loop sparingly** - it runs 200 times per second
7. **Prefer driver events** over polling when possible
8. **Use f-strings** for readable string formatting
9. **Import modules only when needed** to save memory
10. **Use `tasmota.wire_scan()`** instead of manual I²C bus detection