Merge branch 'development' into prerelease-13.1

This commit is contained in:
Theo Arends 2023-08-12 14:25:15 +02:00
commit 5a7a82e975
353 changed files with 29985 additions and 19852 deletions

View File

@ -7,7 +7,7 @@
- [ ] Only relevant files were touched - [ ] Only relevant files were touched
- [ ] Only one feature/fix was added per PR and the code change compiles without warnings - [ ] Only one feature/fix was added per PR and the code change compiles without warnings
- [ ] The code change is tested and works with Tasmota core ESP8266 V.2.7.4.9 - [ ] The code change is tested and works with Tasmota core ESP8266 V.2.7.4.9
- [ ] The code change is tested and works with Tasmota core ESP32 V.2.0.10 - [ ] The code change is tested and works with Tasmota core ESP32 V.2.0.11
- [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). - [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla).
_NOTE: The code change must pass CI tests. **Your PR cannot be merged unless tests pass**_ _NOTE: The code change must pass CI tests. **Your PR cannot be merged unless tests pass**_

View File

@ -119,6 +119,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up
| USE_MGS | - | - / x | - | x | - | - | | USE_MGS | - | - / x | - | x | - | - |
| USE_SGP30 | - | - / x | - | x | - | - | | USE_SGP30 | - | - / x | - | x | - | - |
| USE_SGP40 | - | - / x | - | x | - | - | | USE_SGP40 | - | - / x | - | x | - | - |
| USE_SGP4X | - | - / x | - | - | - | - |
| USE_SEN5X | - | - / x | - | x | - | - | | USE_SEN5X | - | - / x | - | x | - | - |
| USE_SI1145 | - | - / - | - | - | - | - | | USE_SI1145 | - | - / - | - | - | - | - |
| USE_LM75AD | - | - / x | - | x | - | - | | USE_LM75AD | - | - / x | - | x | - | - |
@ -127,6 +128,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up
| USE_MCP23XXX_DRV | - | - / - | - | - | - | - | | USE_MCP23XXX_DRV | - | - / - | - | - | - | - |
| USE_PCA9632 | - | - / - | - | - | - | - | | USE_PCA9632 | - | - / - | - | - | - | - |
| USE_PCA9685 | - | - / - | - | - | - | - | | USE_PCA9685 | - | - / - | - | - | - | - |
| USE_PCA9685_V2 | - | - / - | - | - | - | - |
| USE_MPR121 | - | - / - | - | - | - | - | | USE_MPR121 | - | - / - | - | - | - | - |
| USE_CCS811 | - | - / - | - | x | - | - | | USE_CCS811 | - | - / - | - | x | - | - |
| USE_CCS811_V2 | - | - / x | - | - | - | - | | USE_CCS811_V2 | - | - / x | - | - | - | - |

View File

@ -1,7 +1,92 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [Released] ## [Released] - Development
## [13.1.0] 20230815
- Release Quentin
## [13.0.0.4] 20230815
### Added
- ESP32 prepare for Arduino Core v3 and esp-idf v5 (#19264)
### Changed
- Console height from default 318 pixels to viewport (#19241)
- Shutter button hold behaviour with grouptopic (#19263)
- Thermostat improvements (#19279)
- PID controller improvements (#19285)
## [13.0.0.3] 20230805
### Added
- Support for MAX17043 fuel-gauge systems Lipo batteries (#18788)
- Support for multiple PCA9685 with extended functionality (#18805)
- Zigbee decode Aqara 0000/FF01 attribute 03 as Temperature (#19210)
- Berry bytes `get` and `set` work for 3 bytes values (#19225)
- Matter support for fabric_filtered request (for Google compatibility) (#19249)
### Changed
- Initial ``DisplayMode`` from 1 to 0 and ``DisplayDimmmer`` from 10% to 50% (#19138)
- ESP32 Framework (Arduino Core) from v2.0.10 to v2.0.11
- Berry `mqtt.publish` now distinguishes between `string` and `bytes` (#19196)
- IRremoteESP8266 library from v2.8.5 to v2.8.6
- ESP32 autodetect flashsize and adjust filesystem (#19215)
- Reduced log level for TeleInfo (#19216)
- Matter increased polling frequency for local switches/occupancy (#19242)
### Fixed
- Initial battery level percentage (#19160)
- Berry SK6812_GRBW crash (#19166)
- ESP8266 SPI initialization for scripter, filesystem and MFRC522 (#19209)
- Zero cross dimmer minimum interrupt time (#19211)
- Fade would fail when the difference between start and target would be too small (#19248)
- Inverted shutter (#19243)
- Matter support for large atribute responses (#19252)
- Matter auto-configuration Relay indices (#19255)
## [13.0.0.2] 20230721
### Added
- Partition Wizard is now able to convert to safeboot from Shelly partition layout (#19034)
- Matter mini-profiler (#19075)
- Berry `_class` can be used in `static var` initialization code (#19088)
- Berry `energy.update_total()` to call `EnergyUpdateTotal()` from energy driver (#19117)
- Support for DeepSleep battery level percentage (#19134)
- Berry metrics for memory allocation/deallocation/reallocation (#19150)
- Berry `tasmota.loglevel()` and `tasmota.rtc_utc()` for faster performance (#19152)
- Berry AES CCM decrypting in a single call to avoid any object allocation (#19153)
### Changed
- ESP32 shutter driver support up to 16 shutters (#18295)
- Configuration backup and restore now backup and restore ``.xdrvsetXXX`` files too (#18295)
- Berry extend `range(lower, upper, incr)` to arbitrary increment (#19120)
- Berry updated syntax highlighting plugin for VSCode (#19123)
- Matter latency improvement for single attribute reads and single commands (#19158)
## [13.0.0.1] 20230708
### Added
- Command ``Delay -1`` to wait until next second (#18984)
- Matter add option to disable bridge mode (#18992)
- Support for SGP41 TVOC/NOx Sensor (#18880)
- Command ``BrRestart`` to restart the Berry VM (experimental) (#19003)
- Command ``Restart 9`` to save all changes and go into deepsleep waiting for a reset (#19024)
- Berry added `getgbl` performance counter to `debug.counters()` (#19070)
### Breaking Changed
- Berry `bool( [] )` and `bool( {} )` now evaluate as `false` (#18986)
- Berry `import strict` now detects useless expr without side effects (#18997)
### Changed
- Matter support for temperature in Fahrenheit (`SetOption8 1`) (#18987)
- Matter improve responsiveness (#19002)
- ESP32 LVGL library from v8.3.7 to v8.3.8 (no functional change)
- Matter improve latency for remote commands (#19072)
### Fixed
- Berry various fixes for Walrus Operator (#18982)
- MiElHVAC power commands regression from v12.4.0.1 (#18923)
- `BrRestart` now supports web handlers to work after Berry restart
### Removed
- Support for ESP32-C3 with chip rev below 3 (old development boards)
## [13.0.0] 20230626 ## [13.0.0] 20230626
- Release Qasim - Release Qasim
@ -69,7 +154,7 @@ All notable changes to this project will be documented in this file.
### Added ### Added
- Matter support for Shutters with Tilt - Matter support for Shutters with Tilt
- Matter POC for remote Relay - Matter POC for remote Relay
- Support for Zero-Cross Dimmer on ESP32, changed calculation on EPS8266, high resolution control e.g. Solar: `ZCDimmerSet` - Support for Zero-Cross Dimmer on ESP32, changed calculation on ESP8266, high resolution control e.g. Solar: `ZCDimmerSet`
- ESP32 Enhanced Shutterbuttons functionality to control tilt position, additionally incr/decr possible to position and tilt. - ESP32 Enhanced Shutterbuttons functionality to control tilt position, additionally incr/decr possible to position and tilt.
- ESP32 command ``Shuttersetup`` for "Shelly 2.5 pro" automatic calibration and setup (experimental) - ESP32 command ``Shuttersetup`` for "Shelly 2.5 pro" automatic calibration and setup (experimental)
- Berry `tcpclientasync` class for non-blocking TCP client - Berry `tcpclientasync` class for non-blocking TCP client

View File

@ -80,7 +80,16 @@ In addition to @arendst the following code is mainly owned by:
| xdrv_66_tm1638 | @arendst | xdrv_66_tm1638 | @arendst
| xdrv_67_mcp23xxx | @arendst | xdrv_67_mcp23xxx | @arendst
| xdrv_68_zerocrossDimmer.ino | @stefanbode | xdrv_68_zerocrossDimmer.ino | @stefanbode
| | | xdrv_69_pca9557 | @cctweaker
| xdrv_70 |
| xdrv_71 |
| xdrv_72 |
| xdrv_73 |
| xdrv_74 |
| xdrv_75 |
| xdrv_76 |
| xdrv_77 |
| xdrv_78 |
| xdrv_79_esp32_ble | @staars, @btsimonh | xdrv_79_esp32_ble | @staars, @btsimonh
| xdrv_81_esp32_webcam | @gemu, @philrich | xdrv_81_esp32_webcam | @gemu, @philrich
| xdrv_82_esp32_ethernet | @arendst | xdrv_82_esp32_ethernet | @arendst
@ -208,6 +217,8 @@ In addition to @arendst the following code is mainly owned by:
| xsns_106_gdk101 | @Szewcson | xsns_106_gdk101 | @Szewcson
| xsns_107_gm861 | @arendst | xsns_107_gm861 | @arendst
| xsns_108_tc74 | Michael Loftis | xsns_108_tc74 | Michael Loftis
| xsns_109_sgp4x | Andrew Klaus
| xsns_110_max17043 | Vincent de Groot
| | | |
| Libraries | | Libraries |
| | | |

View File

@ -9,6 +9,7 @@ The following table lists the supported I2C devices
Index | Define | Driver | Device | Address(es) | Description Index | Define | Driver | Device | Address(es) | Description
------|---------------------|----------|----------|-------------|----------------------------------------------- ------|---------------------|----------|----------|-------------|-----------------------------------------------
1 | USE_PCA9685 | xdrv_15 | PCA9685 | 0x40 - 0x47 | 16-channel 12-bit pwm driver 1 | USE_PCA9685 | xdrv_15 | PCA9685 | 0x40 - 0x47 | 16-channel 12-bit pwm driver
1 | USE_PCA9685_V2 | 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 | 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) 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 3 | USE_DISPLAY_LCD | xdsp_01 | | 0x27, 0x3F | LCD display
@ -117,3 +118,5 @@ Index | Define | Driver | Device | Address(es) | Description
79 | USE_GDK101 | xsns_106 | GDK101 | 0x18 - 0x1B | Gamma Radiation Sensor 79 | USE_GDK101 | xsns_106 | GDK101 | 0x18 - 0x1B | Gamma Radiation Sensor
80 | USE_TC74 | xsns_108 | TC74 | 0x48 - 0x4F | Temperature sensor 80 | USE_TC74 | xsns_108 | TC74 | 0x48 - 0x4F | Temperature sensor
81 | USE_PCA9557 | xdrv_69 | PCA95xx | 0x18 - 0x1F | 8-bit I/O expander as virtual button/switch/relay 81 | USE_PCA9557 | xdrv_69 | PCA95xx | 0x18 - 0x1F | 8-bit I/O expander as virtual button/switch/relay
82 | USE_SGP4X | xsns_109 | SGP4X | 0x59 | Gas (TVOC/NOx index)
83 | USE_MAX17043 | xsns_110 | MAX17043 | 0x36 | Fuel-gauge for 3.7 Volt Lipo battery

View File

@ -36,9 +36,9 @@ While fallback or downgrading is common practice it was never supported due to S
This release will be supported from ESP8266/Arduino library Core version **2.7.4.9** due to reported security and stability issues on previous Core version. This will also support gzipped binaries. This release will be supported from ESP8266/Arduino library Core version **2.7.4.9** due to reported security and stability issues on previous Core version. This will also support gzipped binaries.
This release will be supported from ESP32/Arduino library Core version **2.0.10**. This release will be supported from ESP32/Arduino library Core version **2.0.11**.
Support of ESP8266 Core versions before 2.7.4.9 and ESP32 Core versions before 2.0.10 have been removed. Support of ESP8266 Core versions before 2.7.4.9 and ESP32 Core versions before 2.0.11 have been removed.
## Support of TLS ## Support of TLS
@ -75,12 +75,12 @@ Latest released binaries can be downloaded from
- http://ota.tasmota.com/tasmota/release - http://ota.tasmota.com/tasmota/release
Historical binaries can be downloaded from Historical binaries can be downloaded from
- http://ota.tasmota.com/tasmota/release-13.0.0 - http://ota.tasmota.com/tasmota/release-13.1.0
The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz`` The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz``
### ESP32, ESP32-C3, ESP32-S2 and ESP32-S3 based ### ESP32, ESP32-C3, ESP32-S2 and ESP32-S3 based
The following binary downloads have been compiled with ESP32/Arduino library core version **2.0.10**. The following binary downloads have been compiled with ESP32/Arduino library core version **2.0.11**.
- **tasmota32.bin** = The Tasmota version with most drivers including additional sensors and KNX for 4M+ flash. **RECOMMENDED RELEASE BINARY** - **tasmota32.bin** = The Tasmota version with most drivers including additional sensors and KNX for 4M+ flash. **RECOMMENDED RELEASE BINARY**
- **tasmota32xy.bin** = The Tasmota version with most drivers including additional sensors and KNX for ESP32-C3/S2/S3 and 4M+ flash. - **tasmota32xy.bin** = The Tasmota version with most drivers including additional sensors and KNX for ESP32-C3/S2/S3 and 4M+ flash.
@ -100,7 +100,7 @@ Latest released binaries can be downloaded from
- https://ota.tasmota.com/tasmota32/release - https://ota.tasmota.com/tasmota32/release
Historical binaries can be downloaded from Historical binaries can be downloaded from
- https://ota.tasmota.com/tasmota32/release-13.0.0 - https://ota.tasmota.com/tasmota32/release-13.1.0
The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasmota.com/tasmota32/release/tasmota32.bin`` The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasmota.com/tasmota32/release/tasmota32.bin``
@ -110,75 +110,64 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
[Complete list](BUILDS.md) of available feature and sensors. [Complete list](BUILDS.md) of available feature and sensors.
## Changelog v13.0.0 Qasim ## Changelog v13.1.0 Quentin
### Added ### Added
- Command ``SetOption152 0/1`` to select two (0 = default) pin bistable or one (1) pin latching relay control [#18386](https://github.com/arendst/Tasmota/issues/18386) - Command ``BrRestart`` to restart the Berry VM (experimental) [#19003](https://github.com/arendst/Tasmota/issues/19003)
- Command ``I2cScan0`` to scan both busses on ESP32 with one command - Command ``Delay -1`` to wait until next second [#18984](https://github.com/arendst/Tasmota/issues/18984)
- Command ``WifiPower 0`` to enable dynamic wifi power based on RSSI by @TD-er [#15443](https://github.com/arendst/Tasmota/issues/15443) - Command ``Restart 9`` to save all changes and go into deepsleep waiting for a reset [#19024](https://github.com/arendst/Tasmota/issues/19024)
- Command ``WifiPower 1`` to restore default wifi power - Support for MAX17043 fuel-gauge systems Lipo batteries [#18788](https://github.com/arendst/Tasmota/issues/18788)
- Support for TC74 temperature sensor by Michael Loftis [#18042](https://github.com/arendst/Tasmota/issues/18042) - Support for multiple PCA9685 with extended functionality [#18805](https://github.com/arendst/Tasmota/issues/18805)
- Support for GM861 1D and 2D bar code reader [#18399](https://github.com/arendst/Tasmota/issues/18399) - Support for SGP41 TVOC/NOx Sensor [#18880](https://github.com/arendst/Tasmota/issues/18880)
- Support for PCA9557 8-bit I/O expander [#18632](https://github.com/arendst/Tasmota/issues/18632) - Support for DeepSleep battery level percentage [#19134](https://github.com/arendst/Tasmota/issues/19134)
- Display descriptor for ST7735 128x160 display [#18741](https://github.com/arendst/Tasmota/issues/18741) - Zigbee decode Aqara 0000/FF01 attribute 03 as Temperature [#19210](https://github.com/arendst/Tasmota/issues/19210)
- Zigbee support for air sensors [#18665](https://github.com/arendst/Tasmota/issues/18665) - ESP32 prepare for Arduino Core v3 and esp-idf v5 [#19264](https://github.com/arendst/Tasmota/issues/19264)
- Zigbee firmware for Sonoff-ZB-Pro v20230507 [#18968](https://github.com/arendst/Tasmota/issues/18968) - Berry `getgbl` performance counter to `debug.counters()` [#19070](https://github.com/arendst/Tasmota/issues/19070)
- ESP32 command ``Shuttersetup`` for "Shelly 2.5 pro" automatic calibration and setup (experimental) - Berry `_class` can be used in `static var` initialization code [#19088](https://github.com/arendst/Tasmota/issues/19088)
- ESP32 Enhanced Shutterbuttons functionality to control tilt position, additionally incr/decr possible to position and tilt. - Berry `energy.update_total()` to call `EnergyUpdateTotal()` from energy driver [#19117](https://github.com/arendst/Tasmota/issues/19117)
- Berry RS256 crypto algorithm (RSASSA-MCKS1_v1-5 with SHA256) used for JWT [#18763](https://github.com/arendst/Tasmota/issues/18763) - Berry `tasmota.loglevel()` and `tasmota.rtc_utc()` for faster performance [#19152](https://github.com/arendst/Tasmota/issues/19152)
- Berry `tcpclientasync` class for non-blocking TCP client - Berry metrics for memory allocation/deallocation/reallocation [#19150](https://github.com/arendst/Tasmota/issues/19150)
- Berry `set_lsb_justified(bool)` to `AudioOutputI2S` [#18774](https://github.com/arendst/Tasmota/issues/18774) - Berry AES CCM decrypting in a single call to avoid any object allocation [#19153](https://github.com/arendst/Tasmota/issues/19153)
- Berry `string.format()` now automatically converts type according to format [#18890](https://github.com/arendst/Tasmota/issues/18890) - Berry bytes `get` and `set` work for 3 bytes values [#19225](https://github.com/arendst/Tasmota/issues/19225)
- Berry global function `format` as a simpler syntax to `string.format` [#18925](https://github.com/arendst/Tasmota/issues/18925) - Partition Wizard is now able to convert to safeboot from Shelly partition layout [#19034](https://github.com/arendst/Tasmota/issues/19034)
- Berry f-strings as an alternative to string formatting [#18937](https://github.com/arendst/Tasmota/issues/18937) - Matter option to disable bridge mode [#18992](https://github.com/arendst/Tasmota/issues/18992)
- Berry Walrus operator ':=' [#18963](https://github.com/arendst/Tasmota/issues/18963) - Matter mini-profiler [#19075](https://github.com/arendst/Tasmota/issues/19075)
- HASPmota `meta` attribute and improved `berry_run` [#18685](https://github.com/arendst/Tasmota/issues/18685) - Matter support for fabric_filtered request (for Google compatibility) [#19249](https://github.com/arendst/Tasmota/issues/19249)
- Matter sensors Humidity, Pressure, Illuminance [#18441](https://github.com/arendst/Tasmota/issues/18441)
- Matter allow `Matter#Initialized` rule once the device is configured [#18451](https://github.com/arendst/Tasmota/issues/18451)
- Matter UI to change endpoints configuration [#18498](https://github.com/arendst/Tasmota/issues/18498)
- Matter support for Shutters with Tilt [#18509](https://github.com/arendst/Tasmota/issues/18509)
- Matter support for async HTTP used for bridged devices and remote relays [#18656](https://github.com/arendst/Tasmota/issues/18656)
- Matter bridge for ESP8266 remote endpoints (experimental) [#18734](https://github.com/arendst/Tasmota/issues/18734)
- Matter support for Occupancy via Switch (experimental) [#18742](https://github.com/arendst/Tasmota/issues/18742)
- Matter ability to add or remove endpoint in bridge mode (code only) [#18790](https://github.com/arendst/Tasmota/issues/18790)
- Matter controller's Vendor Name to logs and UI [#18794](https://github.com/arendst/Tasmota/issues/18794)
- Matter redesigned UI [#18855](https://github.com/arendst/Tasmota/issues/18855)
- Matter support for Contact Sensor [#18882](https://github.com/arendst/Tasmota/issues/18882)
- Matter friendly-name (NodeLabel) to each endpoint [#18897](https://github.com/arendst/Tasmota/issues/18897)
- Matter display the remote Device Name instead of IP address [#18961](https://github.com/arendst/Tasmota/issues/18961)
### Breaking Changed ### Breaking Changed
- Change command ``FileUpload`` index binary data detection from >199 to >299 - Berry `bool( [] )` and `bool( {} )` now evaluate as `false` [#18986](https://github.com/arendst/Tasmota/issues/18986)
- Matter relay number starts at 1 instead of 0 to match Tasmota numbering - Berry `import strict` now detects useless expression without side effects [#18997](https://github.com/arendst/Tasmota/issues/18997)
### Changed ### Changed
- AdafruitFingerprint library from v2.0.4 to v2.1.0 - IRremoteESP8266 library from v2.8.5 to v2.8.6
- IRremoteESP8266 library from v2.8.4 to v2.8.5 - ESP32 Framework (Arduino Core) from v2.0.10 to v2.0.11
- ESP32 Framework (Core) from v2.0.7 to v2.0.10 - ESP32 LVGL library from v8.3.7 to v8.3.8 (no functional change)
- ESP32 Binaries size increase of 300k due to Matter support may need [Partition Wizard](https://tasmota.github.io/docs/Tasmota-Application/#partition-management) - Initial ``DisplayMode`` from 1 to 0 and ``DisplayDimmmer`` from 10% to 50% [#19138](https://github.com/arendst/Tasmota/issues/19138)
- InfluxDb resolves DNS name before request [#18015](https://github.com/arendst/Tasmota/issues/18015) - Configuration backup and restore now supports ``.xdrvsetXXX`` files too [#18295](https://github.com/arendst/Tasmota/issues/18295)
- Refactored Zero Cross Dimmer [#18481](https://github.com/arendst/Tasmota/issues/18481) - Reduced log level for TeleInfo [#19216](https://github.com/arendst/Tasmota/issues/19216)
- Energy power delta report delayed by two seconds allowing hardware to stabilize [#17751](https://github.com/arendst/Tasmota/issues/17751) - Console height from default 318 pixels to viewport [#19241](https://github.com/arendst/Tasmota/issues/19241)
- Shutter sliders in WEBGUI automatically appear and disappear during configuration and update during movement [#18701](https://github.com/arendst/Tasmota/issues/18701) - Shutter button hold behaviour with grouptopic [#19263](https://github.com/arendst/Tasmota/issues/19263)
- Berry `webclient.url_encode()` is now a static class method, no change required to existing code [#18775](https://github.com/arendst/Tasmota/issues/18775) - Thermostat improvements [#19279](https://github.com/arendst/Tasmota/issues/19279)
- PID controller improvements [#19285](https://github.com/arendst/Tasmota/issues/19285)
- ESP32 shutter driver support up to 16 shutters [#18295](https://github.com/arendst/Tasmota/issues/18295)
- ESP32 autodetect flashsize and adjust filesystem [#19215](https://github.com/arendst/Tasmota/issues/19215)
- Berry extend `range(lower, upper, incr)` to arbitrary increment [#19120](https://github.com/arendst/Tasmota/issues/19120)
- Berry updated syntax highlighting plugin for VSCode [#19123](https://github.com/arendst/Tasmota/issues/19123)
- Berry `mqtt.publish` now distinguishes between `string` and `bytes` [#19196](https://github.com/arendst/Tasmota/issues/19196)
- Matter support for temperature in Fahrenheit (`SetOption8 1`) [#18987](https://github.com/arendst/Tasmota/issues/18987)
- Matter improve responsiveness [#19002](https://github.com/arendst/Tasmota/issues/19002)
- Matter improve latency for remote commands [#19072](https://github.com/arendst/Tasmota/issues/19072)
- Matter improve latency for single attribute reads and single commands [#19158](https://github.com/arendst/Tasmota/issues/19158)
- Matter increased polling frequency for local switches/occupancy [#19242](https://github.com/arendst/Tasmota/issues/19242)
### Fixed ### Fixed
- ESP8266 no update on Energy Export Active regression from v12.3.1.3 - Berry Walrus Operator [#18982](https://github.com/arendst/Tasmota/issues/18982)
- NovaSDS GUI values [#18444](https://github.com/arendst/Tasmota/issues/18444) - MiElHVAC power commands regression from v12.4.0.1 [#18923](https://github.com/arendst/Tasmota/issues/18923)
- LED PWM ac_dimmer curve was wrongly applied instead of Gamma regression from v12.2.0.5 [#18666](https://github.com/arendst/Tasmota/issues/18666) - Zero cross dimmer minimum interrupt time [#19211](https://github.com/arendst/Tasmota/issues/19211)
- Shutter bootloop using more than 4 shutters [#18673](https://github.com/arendst/Tasmota/issues/18673) - Fade would fail when the difference between start and target would be too small [#19248](https://github.com/arendst/Tasmota/issues/19248)
- Inverted shutter now reflect status also in WEBGUI and several minor fixes to make "inverted" consistant [#18701](https://github.com/arendst/Tasmota/issues/18701) - Inverted shutter [#19243](https://github.com/arendst/Tasmota/issues/19243)
- Interaction of ``SetOption92``, ``VirtualCT``, and ``RGBWWTable`` [#18768](https://github.com/arendst/Tasmota/issues/18768) - ESP8266 SPI initialization for scripter, filesystem and MFRC522 [#19209](https://github.com/arendst/Tasmota/issues/19209)
- Freeze BMP readings before deepsleep [#18720](https://github.com/arendst/Tasmota/issues/18720) - Matter support for large atribute responses [#19252](https://github.com/arendst/Tasmota/issues/19252)
- NeoPool NPFiltration switch result [#18871](https://github.com/arendst/Tasmota/issues/18871) - Matter auto-configuration Relay indices [#19255](https://github.com/arendst/Tasmota/issues/19255)
- ESP32 Partition_Manager.tapp
- ESP32 InfluxDb initial connection delays using HTTPClient [#18015](https://github.com/arendst/Tasmota/issues/18015) ### Removed
- ESP32 AIThinker webcam issues [#18652](https://github.com/arendst/Tasmota/issues/18652) - Support for ESP32-C3 with chip revision below 3 (old development boards)
- ESP32 SPI initialization for MFRC522 [#18711](https://github.com/arendst/Tasmota/issues/18711)
- ESP32 Neopixel busy time adjustment [#18723](https://github.com/arendst/Tasmota/issues/18723)
- HASPmota event when value is non-integer [#18229](https://github.com/arendst/Tasmota/issues/18229)
- Berry a rare condition when a GC causes a memory corruption
- Berry rules for string comparisons [#18464](https://github.com/arendst/Tasmota/issues/18464)
- Berry parser error with upvals in closures [#18902](https://github.com/arendst/Tasmota/issues/18902)
- Zigbee attributes handling in Berry mapping [#18747](https://github.com/arendst/Tasmota/issues/18747)
- Zigbee regression with ``SetOption101`` [#18884](https://github.com/arendst/Tasmota/issues/18884)
- Matter fabric provisioning from CASE session for iOS 16.5 [#18709](https://github.com/arendst/Tasmota/issues/18709)

View File

@ -5,7 +5,7 @@
# Templates # Templates
Find below the available templates as of June 2023. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) Find below the available templates as of August 2023. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates)
## Adapter Board ## Adapter Board
``` ```
@ -226,6 +226,7 @@ MS-108 In-Wall {"NAME":"MS-108","GPIO":[0,0,0,0,161,160,0,0,224,0,
MS-108WR RF Curtain Module {"NAME":"MS-108WR","GPIO":[1,1,1,544,32,33,1,1,225,32,224,1,1,1],"FLAG":0,"BASE":18} MS-108WR RF Curtain Module {"NAME":"MS-108WR","GPIO":[1,1,1,544,32,33,1,1,225,32,224,1,1,1],"FLAG":0,"BASE":18}
Nous {"NAME":" Smart WiFi Curtain Module L12T","GPIO":[1,160,1,161,225,224,1,1,544,1,32,1,1,1],"FLAG":0,"BASE":18} Nous {"NAME":" Smart WiFi Curtain Module L12T","GPIO":[1,160,1,161,225,224,1,1,544,1,32,1,1,1],"FLAG":0,"BASE":18}
QS-WIFI-C01-RF {"NAME":"Shutter-QS-WIFI-C01","GPIO":[0,0,1,0,288,0,0,0,32,33,224,225,0,0],"FLAG":0,"BASE":18} QS-WIFI-C01-RF {"NAME":"Shutter-QS-WIFI-C01","GPIO":[0,0,1,0,288,0,0,0,32,33,224,225,0,0],"FLAG":0,"BASE":18}
wesmartify Smart Home Aktor Shut It TASMOTA {"NAME":"ITzy","GPIO":[0,0,0,0,225,224,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
``` ```
## Curtain Switch ## Curtain Switch
@ -257,10 +258,11 @@ Zemismart Backlit {"NAME":"WF-CS01","GPIO":[544,227,289,34,226,161,0,
## DIN Relay ## DIN Relay
``` ```
CurrySmarter Power Monitoring 30A {"NAME":"30A Breaker","GPIO":[0,0,0,0,7584,224,0,0,2720,32,2656,2624,320,0],"FLAG":0,"BASE":18} CurrySmarter Power Monitoring 30A {"NAME":"30A Breaker on Led","GPIO":[0,0,0,0,7584,224,0,0,2720,32,2656,2624,320,0],"FLAG":0,"BASE":18}
EARU DIN Circuit Breaker 1P 32A/50A {"NAME":"RDCBC-1P","GPIO":[320,0,0,0,0,0,0,0,32,224,0,0,0,0],"FLAG":0,"BASE":18} EARU DIN Circuit Breaker 1P 32A/50A {"NAME":"RDCBC-1P","GPIO":[320,0,0,0,0,0,0,0,32,224,0,0,0,0],"FLAG":0,"BASE":18}
Hoch Circuit Breaker 1P {"NAME":"HOCH ZJSB9","GPIO":[32,0,0,0,0,0,0,0,224,320,0,0,0,0],"FLAG":0,"BASE":18} Hoch Circuit Breaker 1P {"NAME":"HOCH ZJSB9","GPIO":[32,0,0,0,0,0,0,0,224,320,0,0,0,0],"FLAG":0,"BASE":18}
Ketotek Single Phase Energy Monitor {"NAME":"Ketotek KTEM06","GPIO":[0,2272,0,2304,0,0,0,0,0,0,320,0,32,0],"FLAG":0,"BASE":54} Ketotek Single Phase Energy Monitor {"NAME":"Ketotek KTEM06","GPIO":[0,2272,0,2304,0,0,0,0,0,0,320,0,32,0],"FLAG":0,"BASE":54}
Martin Jerry 30A Circuit Breaker {"NAME":"30A Breaker","GPIO":[0,0,0,0,7584,224,0,0,2720,32,2656,2624,320,0],"FLAG":0,"BASE":18}
OpenEnergyMonitor WiFi MQTT Thermostat {"NAME":"MQTT-RELAY","GPIO":[32,0,1,0,0,224,0,0,0,0,0,0,320,0],"FLAG":0,"BASE":18} OpenEnergyMonitor WiFi MQTT Thermostat {"NAME":"MQTT-RELAY","GPIO":[32,0,1,0,0,224,0,0,0,0,0,0,320,0],"FLAG":0,"BASE":18}
RocketController ASTRA Controller {"NAME":"ASTRA R4A4","GPIO":[1,1,1,1,576,1,1,1,480,1,1,1,3232,3200,1,1,0,640,608,1,0,224,225,1152,0,0,0,0,227,226,160,161,162,0,0,163],"FLAG":0,"BASE":1} RocketController ASTRA Controller {"NAME":"ASTRA R4A4","GPIO":[1,1,1,1,576,1,1,1,480,1,1,1,3232,3200,1,1,0,640,608,1,0,224,225,1152,0,0,0,0,227,226,160,161,162,0,0,163],"FLAG":0,"BASE":1}
Shelly Pro 1 {"NAME":"Shelly Pro 1","GPIO":[0,1,0,1,768,0,0,0,672,704,736,0,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,0,0,0,32,4736,0,160,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,10000,10000,3350"} Shelly Pro 1 {"NAME":"Shelly Pro 1","GPIO":[0,1,0,1,768,0,0,0,672,704,736,0,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,0,0,0,32,4736,0,160,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,10000,10000,3350"}
@ -295,6 +297,7 @@ Adafruit QT Py ESP32 Pico {"NAME":"QTPy ESP32 Pico","GPIO":[32,3200,0,3232,1,
AZ-Envy Environmental Sensor {"NAME":"AZ Envy","GPIO":[32,0,320,0,640,608,0,0,0,0,0,0,0,4704],"FLAG":0,"BASE":18} AZ-Envy Environmental Sensor {"NAME":"AZ Envy","GPIO":[32,0,320,0,640,608,0,0,0,0,0,0,0,4704],"FLAG":0,"BASE":18}
Coiaca Tasmota {"NAME":"AWR01t","GPIO":[576,1,1,128,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} Coiaca Tasmota {"NAME":"AWR01t","GPIO":[576,1,1,128,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Coiaca Tasmota Development Board AWR12 {"NAME":"AWR12t","GPIO":[320,1,1,1,1,1,0,0,1,1,1,1,1,1],"FLAG":0,"BASE":18} Coiaca Tasmota Development Board AWR12 {"NAME":"AWR12t","GPIO":[320,1,1,1,1,1,0,0,1,1,1,1,1,1],"FLAG":0,"BASE":18}
Dasduino CONNECTPLUS {"NAME":"Dasduino CONNECT","GPIO":[1,1,1376,1,640,608,1,1,1,1,1,1,1,1],"FLAG":0,"BASE":18}
Dasduino CONNECTPLUS {"NAME":"Dasduino CONNECTPLUS","GPIO":[32,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,640,608,1,0,1,1,1,0,0,0,0,1376,1,1,1,1,0,0,1],"FLAG":0,"BASE":1} Dasduino CONNECTPLUS {"NAME":"Dasduino CONNECTPLUS","GPIO":[32,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,640,608,1,0,1,1,1,0,0,0,0,1376,1,1,1,1,0,0,1],"FLAG":0,"BASE":1}
Espoir Rev 1.0.0 PoE+ {"NAME":"Espoir","GPIO":[0,0,1,0,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,5568,5600,1,7968,1,1,1,1],"FLAG":0,"BASE":1} Espoir Rev 1.0.0 PoE+ {"NAME":"Espoir","GPIO":[0,0,1,0,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,5568,5600,1,7968,1,1,1,1],"FLAG":0,"BASE":1}
KinCony 128 Channel Controller {"NAME":"KC868-A128","GPIO":[0,1,0,1,609,640,1,1,1,3232,3200,641,608,1,5600,0,0,1,0,5568,0,1,0,0,0,0,0,0,0,0,4705,4707,4706,0,0,4704],"FLAG":0,"BASE":1} KinCony 128 Channel Controller {"NAME":"KC868-A128","GPIO":[0,1,0,1,609,640,1,1,1,3232,3200,641,608,1,5600,0,0,1,0,5568,0,1,0,0,0,0,0,0,0,0,4705,4707,4706,0,0,4704],"FLAG":0,"BASE":1}
@ -392,7 +395,7 @@ Shelly Vintage 7W 750lm 2700k {"NAME":"Shelly Vintage","GPIO":[0,0,0,0,416,0,0,
Shelly Vintage 7W 750lm 2700k {"NAME":"Shelly Vintage","GPIO":[0,0,0,0,416,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} Shelly Vintage 7W 750lm 2700k {"NAME":"Shelly Vintage","GPIO":[0,0,0,0,416,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
SmartDGM 9W 806lm {"NAME":"L-WB9W1","GPIO":[0,0,0,0,0,416,0,0,160,0,0,0,0,0],"FLAG":0,"BASE":18} SmartDGM 9W 806lm {"NAME":"L-WB9W1","GPIO":[0,0,0,0,0,416,0,0,160,0,0,0,0,0],"FLAG":0,"BASE":18}
Smitch 10W 6500K {"NAME":"Smitch Ambience SB-0110","GPIO":[0,0,0,0,416,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} Smitch 10W 6500K {"NAME":"Smitch Ambience SB-0110","GPIO":[0,0,0,0,416,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Smitch 10W 6500K {"NAME":"Smitch Ambience SB-0110","GPIO":[0,0,0,0,416,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} Smitch 10W 6500K {"NAME":"Smitch 10W 6500K Dimmable Bulb (SB0110 - E27)","GPIO":[0,0,0,0,0,416,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":1}
TCP Smart 806lm Warm White {"NAME":"TCP Smart Clas","GPIO":[0,0,0,0,0,0,0,0,0,416,0,0,0,0],"FLAG":0,"BASE":1} TCP Smart 806lm Warm White {"NAME":"TCP Smart Clas","GPIO":[0,0,0,0,0,0,0,0,0,416,0,0,0,0],"FLAG":0,"BASE":1}
TCP Smart 810lm Filament {"NAME":"TCP Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,448,0,0,0],"FLAG":0,"BASE":18} TCP Smart 810lm Filament {"NAME":"TCP Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,448,0,0,0],"FLAG":0,"BASE":18}
TCP Smart 810lm Filament {"NAME":"TCP Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,448,0,0,0],"FLAG":0,"BASE":18} TCP Smart 810lm Filament {"NAME":"TCP Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,448,0,0,0],"FLAG":0,"BASE":18}
@ -605,6 +608,7 @@ AI Universal Remote {"NAME":"YTF IR Controller","GPIO":[1,1,1,1,320,108
AI Universal Remote Control {"NAME":"LQ-08","GPIO":[0,0,0,0,0,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":62} AI Universal Remote Control {"NAME":"LQ-08","GPIO":[0,0,0,0,0,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":62}
Alfawise KS1 {"NAME":"KS1","GPIO":[1,1792,32,1824,32,1088,0,0,320,0,1056,0,0,4704],"FLAG":0,"BASE":62} Alfawise KS1 {"NAME":"KS1","GPIO":[1,1792,32,1824,32,1088,0,0,320,0,1056,0,0,4704],"FLAG":0,"BASE":62}
Antsig Universal Remote Controller {"NAME":"Antsig Smart Wi-Fi IR","GPIO":[1,1,1,1,320,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":62} Antsig Universal Remote Controller {"NAME":"Antsig Smart Wi-Fi IR","GPIO":[1,1,1,1,320,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":62}
Athom {"NAME":"Athom_IR_Remote","GPIO":[32,0,0,0,1056,1088,0,0,0,576,0,0,0,0],"FLAG":0,"BASE":18}
Automate Things IR Bridge {"NAME":"AT-IRBR-1.0","GPIO":[0,0,0,0,1056,1088,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18,"CMND":"Module 0"} Automate Things IR Bridge {"NAME":"AT-IRBR-1.0","GPIO":[0,0,0,0,1056,1088,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18,"CMND":"Module 0"}
Automate Things IR Bridge {"NAME":"AT-IRBR-1.4","GPIO":[1088,0,0,0,1056,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18,"CMND":"Module 0"} Automate Things IR Bridge {"NAME":"AT-IRBR-1.4","GPIO":[1088,0,0,0,1056,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18,"CMND":"Module 0"}
auvisio S06 {"NAME":"NX-4519-675","GPIO":[0,0,0,0,288,1088,0,0,0,0,1056,0,0,0],"FLAG":0,"BASE":18} auvisio S06 {"NAME":"NX-4519-675","GPIO":[0,0,0,0,288,1088,0,0,0,0,1056,0,0,0],"FLAG":0,"BASE":18}
@ -710,6 +714,7 @@ ZJ-WF-ESP-A v1.1 {"NAME":"RGB2","GPIO":[0,0,0,0,0,0,0,0,417,416,418,
## LED Strip ## LED Strip
``` ```
Aldi Casalux RGB {"NAME":"DW-RGB-WI01","GPIO":[1088,0,0,0,416,0,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18} Aldi Casalux RGB {"NAME":"DW-RGB-WI01","GPIO":[1088,0,0,0,416,0,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18}
ARLEC 5m Colour Changing "Not available"
Arlec Smart 1m CCT LED Strip Light {"NAME":"ALD155HA","GPIO":[0,0,1088,0,0,416,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18} Arlec Smart 1m CCT LED Strip Light {"NAME":"ALD155HA","GPIO":[0,0,1088,0,0,416,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18}
Arlec Smart 2m LED Colour Changing Strip Light {"NAME":"Arlec_Light_Strip","GPIO":[1,1,1088,1,416,419,1,1,417,420,418,0,1,1],"FLAG":0,"BASE":18} Arlec Smart 2m LED Colour Changing Strip Light {"NAME":"Arlec_Light_Strip","GPIO":[1,1,1088,1,416,419,1,1,417,420,418,0,1,1],"FLAG":0,"BASE":18}
Arlec Smart 5m White & Colour Changing {"NAME":"ALD556HA","GPIO":[0,0,1088,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} Arlec Smart 5m White & Colour Changing {"NAME":"ALD556HA","GPIO":[0,0,1088,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18}
@ -794,7 +799,7 @@ Deta 18W 1900lm T8 Tube {"NAME":"DETA Smart LED","GPIO":[0,0,0,0,0,0,0,0,0,
electriQ MOODL Ambiance Lamp {"NAME":"ElectriQ MOODL","GPIO":[0,4640,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} electriQ MOODL Ambiance Lamp {"NAME":"ElectriQ MOODL","GPIO":[0,4640,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Gosund Table Lamp {"NAME":"Gosund LB3","GPIO":[0,0,0,0,0,418,0,0,417,419,0,416,0,0],"FLAG":0,"BASE":18} Gosund Table Lamp {"NAME":"Gosund LB3","GPIO":[0,0,0,0,0,418,0,0,417,419,0,416,0,0],"FLAG":0,"BASE":18}
Hama Wall Light Square, 10 cm, IP 44 {"NAME":"Hama Wifi Wall Light","GPIO":[0,0,0,0,0,416,0,0,0,417,0,0,0,1],"FLAG":0,"BASE":18} Hama Wall Light Square, 10 cm, IP 44 {"NAME":"Hama Wifi Wall Light","GPIO":[0,0,0,0,0,416,0,0,0,417,0,0,0,1],"FLAG":0,"BASE":18}
HiFree Table Lamp {"NAME":"TuyaMCU","GPIO":[108,1,107,1,1,1,1,1,1,1,1,1,1,1],"FLAG":0,"BASE":18} HiFree Table Lamp {"NAME":"TuyaMCU","GPIO":[2304,1184,2272,1184,1184,1184,1184,1184,1184,1184,1184,1184,1184,0],"FLAG":0,"BASE":18}
Hugoai Table Lamp {"NAME":"HG02","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 11,20 | TuyaMCU 26,21 | TuyaMCU 21,22 | TuyaMCU 23,23 | TuyaMCU 24,24 | DimmerRange 34,1000"} Hugoai Table Lamp {"NAME":"HG02","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 11,20 | TuyaMCU 26,21 | TuyaMCU 21,22 | TuyaMCU 23,23 | TuyaMCU 24,24 | DimmerRange 34,1000"}
Iwoole Table Lamp {"NAME":"GLOBELAMP","GPIO":[0,0,0,0,419,0,0,0,417,418,416,0,0,0],"FLAG":0,"BASE":18} Iwoole Table Lamp {"NAME":"GLOBELAMP","GPIO":[0,0,0,0,419,0,0,0,417,418,416,0,0,0],"FLAG":0,"BASE":18}
Lepro Bedroom Lamp {"NAME":"Lepro 902101-US","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 11,20 | TuyaMCU 26,21 | TuyaMCU 21,22 | TuyaMCU 23,23 | TuyaMCU 24,24 | DimmerRange 34,1000"} Lepro Bedroom Lamp {"NAME":"Lepro 902101-US","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 11,20 | TuyaMCU 26,21 | TuyaMCU 21,22 | TuyaMCU 23,23 | TuyaMCU 24,24 | DimmerRange 34,1000"}
@ -866,6 +871,7 @@ Xystec USB3.0 4 Port Hub {"NAME":"Xystec USB Hub","GPIO":[0,0,0,0,224,0,0,0,
DT-Light ESP8285 Lighting {"NAME":"DMP-L1","GPIO":[1,1,0,1,1,1,0,0,1,1,1,1,1,1],"FLAG":0,"BASE":18} DT-Light ESP8285 Lighting {"NAME":"DMP-L1","GPIO":[1,1,0,1,1,1,0,0,1,1,1,1,1,1],"FLAG":0,"BASE":18}
ESP-01D {"NAME":"ESP-01D","GPIO":[1,1,0,1,1,0,0,0,1,0,1,0,0,0],"FLAG":0,"BASE":18} ESP-01D {"NAME":"ESP-01D","GPIO":[1,1,0,1,1,0,0,0,1,0,1,0,0,0],"FLAG":0,"BASE":18}
ESP-01S {"NAME":"ESP-01","GPIO":[1,1,1,1,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} ESP-01S {"NAME":"ESP-01","GPIO":[1,1,1,1,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
ESP-02S TYWE2S Replacement {"NAME":"ESP-02S","GPIO":[1,1,1,1,1,1,0,0,1,1,1,0,0,1],"FLAG":0,"BASE":18}
ESP-12 {"NAME":"ESP-12","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,1],"FLAG":0,"BASE":18} ESP-12 {"NAME":"ESP-12","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,1,1],"FLAG":0,"BASE":18}
ESP-15F {"NAME":"ESP-15F","GPIO":[1,1,0,1,1,1,0,0,0,544,0,0,0,0],"FLAG":0,"BASE":18} ESP-15F {"NAME":"ESP-15F","GPIO":[1,1,0,1,1,1,0,0,0,544,0,0,0,0],"FLAG":0,"BASE":18}
ESP-M2 {"NAME":"ESP-M2","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,0,1],"FLAG":0,"BASE":18} ESP-M2 {"NAME":"ESP-M2","GPIO":[1,1,1,1,1,1,0,0,1,1,1,1,0,1],"FLAG":0,"BASE":18}
@ -877,7 +883,6 @@ M5Stack M5Stamp Pico {"NAME":"M5Stamp Pico","GPIO":[1,1,0,1,0,0,0,0,0,0,
MTools 16 Channel ESP32 Relay Driver 5V DC {"NAME":"16ch Board","GPIO":[1,1,237,1,232,1,1,1,228,231,1,1,233,230,234,235,0,238,239,236,0,224,227,226,0,0,0,0,229,225,1,1,1,0,0,1],"FLAG":0,"BASE":1} MTools 16 Channel ESP32 Relay Driver 5V DC {"NAME":"16ch Board","GPIO":[1,1,237,1,232,1,1,1,228,231,1,1,233,230,234,235,0,238,239,236,0,224,227,226,0,0,0,0,229,225,1,1,1,0,0,1],"FLAG":0,"BASE":1}
Shelly Universal Input/Output {"NAME":"Shelly Uni","GPIO":[320,0,0,0,225,1216,0,0,192,193,194,224,0,4864],"FLAG":0,"BASE":18} Shelly Universal Input/Output {"NAME":"Shelly Uni","GPIO":[320,0,0,0,225,1216,0,0,192,193,194,224,0,4864],"FLAG":0,"BASE":18}
Sinilink MODBUS Interface {"NAME":"XY-WFPOW","GPIO":[0,8768,544,8800,32,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} Sinilink MODBUS Interface {"NAME":"XY-WFPOW","GPIO":[0,8768,544,8800,32,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
TYWE2S Replacement {"NAME":"ESP-02S","GPIO":[1,1,1,1,1,1,0,0,1,1,1,0,0,1],"FLAG":0,"BASE":18}
``` ```
## Motion Sensor ## Motion Sensor
@ -946,6 +951,7 @@ Ledvance Smart+ 16A {"NAME":"LEDVANCE Smart Wifi Outdoor Plug","GPIO":[
Ledvance Smart+ Compact {"NAME":"LEDVANCE SMART+ Compact Outdoor Plug ","GPIO":[0,0,0,0,320,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Ledvance Smart+ Compact {"NAME":"LEDVANCE SMART+ Compact Outdoor Plug ","GPIO":[0,0,0,0,320,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
LSC Dual Socket {"NAME":"LSC NFL-022","GPIO":[0,0,0,0,320,32,0,0,0,224,225,0,0,0],"FLAG":0,"BASE":18} LSC Dual Socket {"NAME":"LSC NFL-022","GPIO":[0,0,0,0,320,32,0,0,0,224,225,0,0,0],"FLAG":0,"BASE":18}
LSC Dual Socket {"NAME":"LSC Outdoor Dual Socket","GPIO":[320,0,0,32,8673,8672,0,0,0,0,8674,0,8675,0],"FLAG":0,"BASE":18} LSC Dual Socket {"NAME":"LSC Outdoor Dual Socket","GPIO":[320,0,0,32,8673,8672,0,0,0,0,8674,0,8675,0],"FLAG":0,"BASE":18}
Luminea 16A {"NAME":"NX-4655-675","GPIO":[0,0,0,0,320,576,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Luminea 2 Outlet {"NAME":"Luminea","GPIO":[0,0,0,0,225,320,0,0,224,321,32,0,0,1],"FLAG":0,"BASE":18} Luminea 2 Outlet {"NAME":"Luminea","GPIO":[0,0,0,0,225,320,0,0,224,321,32,0,0,1],"FLAG":0,"BASE":18}
Luminea NX-4458 {"NAME":"Luminea NX4458","GPIO":[32,0,0,0,2688,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":65} Luminea NX-4458 {"NAME":"Luminea NX4458","GPIO":[32,0,0,0,2688,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":65}
Master {"NAME":"Master_IOT-EXTPLUG","GPIO":[32,1,0,1,1,0,0,0,224,288,0,0,0,0],"FLAG":0,"BASE":1} Master {"NAME":"Master_IOT-EXTPLUG","GPIO":[32,1,0,1,1,0,0,0,224,288,0,0,0,0],"FLAG":0,"BASE":1}
@ -968,6 +974,7 @@ Signstek EOP03-EU {"NAME":"Signstek EOP03","GPIO":[0,0,0,0,320,321,0,
SK03 {"NAME":"SK03 Outdoor","GPIO":[32,0,0,0,2688,2656,0,0,2624,321,320,224,0,0],"FLAG":0,"BASE":57} SK03 {"NAME":"SK03 Outdoor","GPIO":[32,0,0,0,2688,2656,0,0,2624,321,320,224,0,0],"FLAG":0,"BASE":57}
STITCH {"NAME":"STITCH 35556","GPIO":[1,1,1,1,225,321,0,0,224,320,32,1,1,0],"FLAG":0,"BASE":18} STITCH {"NAME":"STITCH 35556","GPIO":[1,1,1,1,225,321,0,0,224,320,32,1,1,0],"FLAG":0,"BASE":18}
Suraielec 40A Heavy Duty {"NAME":"Suraielec UBTW01B","GPIO":[0,0,0,0,544,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Suraielec 40A Heavy Duty {"NAME":"Suraielec UBTW01B","GPIO":[0,0,0,0,544,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Tate Guard ZUM-26EU-X Dual {"NAME":"Tate Guard","GPIO":[0,0,0,0,224,288,0,0,225,288,32,0,0,0],"FLAG":0,"BASE":18}
Teckin SS31 {"NAME":"Teckin SS31","GPIO":[1,1,1,1,320,321,1,1,224,32,225,1,1,1],"FLAG":0,"BASE":18} Teckin SS31 {"NAME":"Teckin SS31","GPIO":[1,1,1,1,320,321,1,1,224,32,225,1,1,1],"FLAG":0,"BASE":18}
Teckin SS33 {"NAME":"Teckin SS31","GPIO":[0,0,0,226,320,321,0,0,224,32,225,0,0,1],"FLAG":0,"BASE":18} Teckin SS33 {"NAME":"Teckin SS31","GPIO":[0,0,0,226,320,321,0,0,224,32,225,0,0,1],"FLAG":0,"BASE":18}
Teckin SS42 {"NAME":"Teckin SS42","GPIO":[0,0,0,0,320,321,0,0,224,32,225,0,0,0],"FLAG":0,"BASE":18} Teckin SS42 {"NAME":"Teckin SS42","GPIO":[0,0,0,0,320,321,0,0,224,32,225,0,0,0],"FLAG":0,"BASE":18}
@ -1069,6 +1076,7 @@ Avatto 10A {"NAME":"Avatto NAS-WR01W 10A 2021-12","GPIO":[0,0,
Avatto JH-G01E {"NAME":"AVATTO JH-G01E","GPIO":[0,3072,0,3104,0,0,0,0,32,320,224,0,0,0],"FLAG":0,"BASE":41} Avatto JH-G01E {"NAME":"AVATTO JH-G01E","GPIO":[0,3072,0,3104,0,0,0,0,32,320,224,0,0,0],"FLAG":0,"BASE":41}
Avatto OT06 16A {"NAME":"Avatto OT06","GPIO":[32,0,0,0,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":49} Avatto OT06 16A {"NAME":"Avatto OT06","GPIO":[32,0,0,0,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":49}
Avatto OT08 {"NAME":"Avatto OT08","GPIO":[416,0,418,0,417,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18} Avatto OT08 {"NAME":"Avatto OT08","GPIO":[416,0,418,0,417,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18}
Avidsen Home {"NAME":"Avidsen HomePlug","GPIO":[0,0,0,0,224,35,0,0,289,288,0,0,0,0],"FLAG":0,"BASE":18}
Awow X5P {"NAME":"Awow","GPIO":[0,0,320,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18} Awow X5P {"NAME":"Awow","GPIO":[0,0,320,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18}
AWP02L-N {"NAME":"AWP02L-N","GPIO":[0,0,320,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18} AWP02L-N {"NAME":"AWP02L-N","GPIO":[0,0,320,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18}
AzpenHome Smart {"NAME":"Socket2Me","GPIO":[288,1,1,1,225,1,0,0,224,1,32,1,1,0],"FLAG":0,"BASE":18} AzpenHome Smart {"NAME":"Socket2Me","GPIO":[288,1,1,1,225,1,0,0,224,1,32,1,1,0],"FLAG":0,"BASE":18}
@ -1137,6 +1145,7 @@ Coosa SP1 {"NAME":"COOSA SP1","GPIO":[321,1,320,1,0,2720,0,0,
CooWoo {"NAME":"CooWoo AW01","GPIO":[0,0,0,0,288,160,0,0,256,0,0,0,0,0],"FLAG":0,"BASE":18} CooWoo {"NAME":"CooWoo AW01","GPIO":[0,0,0,0,288,160,0,0,256,0,0,0,0,0],"FLAG":0,"BASE":18}
CozyLife HomeKit 16A {"NAME":"CozyLife 16A","GPIO":[0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,2720,0,0,2656,576,0,224,2624,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} CozyLife HomeKit 16A {"NAME":"CozyLife 16A","GPIO":[0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,2720,0,0,2656,576,0,224,2624,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1}
CrazyLynX WiFi {"NAME":"CrazyLynX","GPIO":[0,0,0,0,321,320,0,0,224,32,0,0,0,4704],"FLAG":0,"BASE":18} CrazyLynX WiFi {"NAME":"CrazyLynX","GPIO":[0,0,0,0,321,320,0,0,224,32,0,0,0,4704],"FLAG":0,"BASE":18}
Crest Single Power Adaptor {"NAME":"SHSPM1","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":52}
Crest Smart Home Single Power Adaptor with 2 USB {"NAME":"Medion","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":52} Crest Smart Home Single Power Adaptor with 2 USB {"NAME":"Medion","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":52}
CurrySmarter 16A {"NAME":"CurrySmarter 16A","GPIO":[0,0,0,32,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18} CurrySmarter 16A {"NAME":"CurrySmarter 16A","GPIO":[0,0,0,32,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18}
CurrySmarter 16A Power Monitoring {"NAME":"Currysmarter XH-TW2P","GPIO":[0,0,0,2624,32,320,0,0,224,2720,2656,0,0,0],"FLAG":0,"BASE":18} CurrySmarter 16A Power Monitoring {"NAME":"Currysmarter XH-TW2P","GPIO":[0,0,0,2624,32,320,0,0,224,2720,2656,0,0,0],"FLAG":0,"BASE":18}
@ -1189,8 +1198,6 @@ Etekcity 15A {"NAME":"ESW15-US","GPIO":[0,0,0,0,0,224,0,0,2656,2
Etekcity 8A {"NAME":"ESW01-USA","GPIO":[0,0,0,0,224,544,0,0,2656,2688,32,2592,288,0],"FLAG":0,"BASE":55} Etekcity 8A {"NAME":"ESW01-USA","GPIO":[0,0,0,0,224,544,0,0,2656,2688,32,2592,288,0],"FLAG":0,"BASE":55}
EU3S {"NAME":"AWOW BSD33","GPIO":[0,0,320,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18} EU3S {"NAME":"AWOW BSD33","GPIO":[0,0,320,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18}
Eva Logik {"NAME":"EVA LOGIK Plug","GPIO":[1,32,1,1,1,1,0,0,1,288,224,1,1,0],"FLAG":0,"BASE":18} Eva Logik {"NAME":"EVA LOGIK Plug","GPIO":[1,32,1,1,1,1,0,0,1,288,224,1,1,0],"FLAG":0,"BASE":18}
EZPlug V1 OpenSource {"NAME":"EZPlug V1","GPIO":[0,0,0,32,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":1}
EZPlug+ V1 {"NAME":"EZPlug+ V1","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":1}
Febite {"NAME":"Febite","GPIO":[320,0,0,0,0,2720,0,0,224,32,2656,0,0,0],"FLAG":0,"BASE":1} Febite {"NAME":"Febite","GPIO":[320,0,0,0,0,2720,0,0,224,32,2656,0,0,0],"FLAG":0,"BASE":1}
Feit Electric PLUG/WIFI {"NAME":"Feit Wifi Plug","GPIO":[0,0,0,320,0,0,0,0,224,0,32,0,0,0],"FLAG":0,"BASE":18} Feit Electric PLUG/WIFI {"NAME":"Feit Wifi Plug","GPIO":[0,0,0,320,0,0,0,0,224,0,32,0,0,0],"FLAG":0,"BASE":18}
FK-PW901U {"NAME":"FK-PW901U","GPIO":[320,1,1,1,1,226,0,0,224,32,227,225,1,0],"FLAG":0,"BASE":18} FK-PW901U {"NAME":"FK-PW901U","GPIO":[320,1,1,1,1,226,0,0,224,32,227,225,1,0],"FLAG":0,"BASE":18}
@ -1311,6 +1318,7 @@ JuoYou 16A {"NAME":"Juoyou ZY-OYD","GPIO":[0,0,0,32,2720,2656,
JVMAC-EU01 {"NAME":"JVMAC","GPIO":[0,32,0,0,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18} JVMAC-EU01 {"NAME":"JVMAC","GPIO":[0,32,0,0,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18}
Kaforto KW-US-801 {"NAME":"Kaforto US-801","GPIO":[32,576,0,227,2720,2656,0,0,2624,225,224,226,0,0],"FLAG":0,"BASE":18} Kaforto KW-US-801 {"NAME":"Kaforto US-801","GPIO":[32,576,0,227,2720,2656,0,0,2624,225,224,226,0,0],"FLAG":0,"BASE":18}
Kauf esphome {"NAME":"KAUF Plug","GPIO":[576,0,320,0,224,2720,0,0,2624,32,2656,0,0,0],"FLAG":0,"BASE":18} Kauf esphome {"NAME":"KAUF Plug","GPIO":[576,0,320,0,224,2720,0,0,2624,32,2656,0,0,0],"FLAG":0,"BASE":18}
Kauf esphome {"NAME":"Kauf Plug","GPIO":[0,320,0,32,2720,2656,0,0,321,224,2624,0,0,0],"FLAG":0,"BASE":18}
Kimire S12 {"NAME":"Kimire S12","GPIO":[1,1,1,32,1,1,0,0,1,320,224,1,1,0],"FLAG":0,"BASE":18} Kimire S12 {"NAME":"Kimire S12","GPIO":[1,1,1,32,1,1,0,0,1,320,224,1,1,0],"FLAG":0,"BASE":18}
King-Link KL-US-WF002 {"NAME":"Kinglink-plug","GPIO":[0,0,0,0,0,224,0,0,288,32,0,0,0,0],"FLAG":0,"BASE":18} King-Link KL-US-WF002 {"NAME":"Kinglink-plug","GPIO":[0,0,0,0,0,224,0,0,288,32,0,0,0,0],"FLAG":0,"BASE":18}
Kisslink SP200 {"NAME":"Kisslink SP200","GPIO":[0,0,0,0,320,321,0,0,224,32,0,0,0,4704],"FLAG":0,"BASE":18} Kisslink SP200 {"NAME":"Kisslink SP200","GPIO":[0,0,0,0,320,321,0,0,224,32,0,0,0,4704],"FLAG":0,"BASE":18}
@ -1408,8 +1416,8 @@ NGS Loop Track 16A {"NAME":"LOOP","GPIO":[0,0,320,0,0,0,0,0,0,32,0,224
Nightlight and AC Outlet {"NAME":"SWN03","GPIO":[32,0,0,0,0,0,1,1,416,0,0,224,0,0],"FLAG":0,"BASE":18} Nightlight and AC Outlet {"NAME":"SWN03","GPIO":[32,0,0,0,0,0,1,1,416,0,0,224,0,0],"FLAG":0,"BASE":18}
Nishica SM-PW701I {"NAME":"SM-PW701I","GPIO":[1,1,1,1,1,1,1,1,224,288,32,1,1,1],"FLAG":0,"BASE":18} Nishica SM-PW701I {"NAME":"SM-PW701I","GPIO":[1,1,1,1,1,1,1,1,224,288,32,1,1,1],"FLAG":0,"BASE":18}
Nivian {"NAME":"Nivian Smart Socket","GPIO":[0,0,320,0,0,2688,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18} Nivian {"NAME":"Nivian Smart Socket","GPIO":[0,0,320,0,0,2688,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18}
Nous 16A {"NAME":"NOUS A1T","GPIO":[32,0,0,0,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":49}
Nous A1 {"NAME":"NOUS A1","GPIO":[320,0,576,0,2656,2720,0,0,2624,32,0,224,0,0],"FLAG":0,"BASE":45} Nous A1 {"NAME":"NOUS A1","GPIO":[320,0,576,0,2656,2720,0,0,2624,32,0,224,0,0],"FLAG":0,"BASE":45}
Nous A1T 16A {"NAME":"NOUS A1T","GPIO":[32,0,0,0,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":49}
NX-SM112 {"NAME":"NX-SM112v3","GPIO":[0,0,0,0,2720,2656,0,0,576,32,2592,224,0,0],"FLAG":0,"BASE":45} NX-SM112 {"NAME":"NX-SM112v3","GPIO":[0,0,0,0,2720,2656,0,0,576,32,2592,224,0,0],"FLAG":0,"BASE":45}
NX-SM200 {"NAME":"NX-SM200","GPIO":[320,0,0,0,0,2720,0,0,224,32,2656,321,2624,0],"FLAG":0,"BASE":18} NX-SM200 {"NAME":"NX-SM200","GPIO":[320,0,0,0,0,2720,0,0,224,32,2656,321,2624,0],"FLAG":0,"BASE":18}
NX-SM210 {"NAME":"NX-SM210","GPIO":[0,32,0,0,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18} NX-SM210 {"NAME":"NX-SM210","GPIO":[0,32,0,0,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18}
@ -1417,7 +1425,7 @@ NX-SM223 {"NAME":"Smart Thurmm","GPIO":[0,32,0,0,0,0,0,0,0,3
Oakter Oak Plug Plus 16A {"NAME":"Oakter OakPlug Plus","GPIO":[0,0,0,0,224,0,0,0,544,320,0,0,0,0],"FLAG":0,"BASE":18} Oakter Oak Plug Plus 16A {"NAME":"Oakter OakPlug Plus","GPIO":[0,0,0,0,224,0,0,0,544,320,0,0,0,0],"FLAG":0,"BASE":18}
Oakter OakPlug Mini 10A {"NAME":"Oakter OakPlug Mini","GPIO":[0,0,0,0,224,0,0,0,544,320,0,0,0,0],"FLAG":0,"BASE":18} Oakter OakPlug Mini 10A {"NAME":"Oakter OakPlug Mini","GPIO":[0,0,0,0,224,0,0,0,544,320,0,0,0,0],"FLAG":0,"BASE":18}
Oakter OakPlug Plus (old) {"NAME":"Oakter OakPlug Plus (old)","GPIO":[0,0,0,0,224,0,0,0,0,320,0,0,544,0],"FLAG":0,"BASE":18} Oakter OakPlug Plus (old) {"NAME":"Oakter OakPlug Plus (old)","GPIO":[0,0,0,0,224,0,0,0,0,320,0,0,544,0],"FLAG":0,"BASE":18}
Obi Stecker {"NAME":"OBI Socket","GPIO":[1,1,0,1,288,224,0,0,290,1,32,0,1,4704],"FLAG":0,"BASE":51} Obi Stecker {"NAME":"Euromate","GPIO":[1,1,1,1,288,224,1,1,289,1,32,1,1,1],"FLAG":0,"BASE":18}
Obi Stecker 2 {"NAME":"OBI Socket 2","GPIO":[0,0,0,0,224,32,0,0,320,289,0,0,0,0],"FLAG":0,"BASE":61} Obi Stecker 2 {"NAME":"OBI Socket 2","GPIO":[0,0,0,0,224,32,0,0,320,289,0,0,0,0],"FLAG":0,"BASE":61}
OFFONG 16A {"NAME":"OFFONG P1","GPIO":[0,32,0,0,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":18} OFFONG 16A {"NAME":"OFFONG P1","GPIO":[0,32,0,0,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":18}
Oittm Smart {"NAME":"Oittm","GPIO":[0,0,0,0,224,320,0,0,32,0,0,0,0,0],"FLAG":0,"BASE":1} Oittm Smart {"NAME":"Oittm","GPIO":[0,0,0,0,224,320,0,0,32,0,0,0,0,0],"FLAG":0,"BASE":1}
@ -1437,6 +1445,7 @@ OxaOxe NX-SP202 v2 {"NAME":"oxaoxe-dold","GPIO":[320,0,0,2624,32,2720,
OZWI Smart {"NAME":"OZWI Smart Plug","GPIO":[0,0,0,0,288,0,0,0,224,32,544,0,0,0],"FLAG":0,"BASE":18} OZWI Smart {"NAME":"OZWI Smart Plug","GPIO":[0,0,0,0,288,0,0,0,224,32,544,0,0,0],"FLAG":0,"BASE":18}
Panamalar Nightlight {"NAME":"Panamalar EWN0","GPIO":[32,0,0,0,0,0,1,1,416,0,0,224,0,0],"FLAG":0,"BASE":18} Panamalar Nightlight {"NAME":"Panamalar EWN0","GPIO":[32,0,0,0,0,0,1,1,416,0,0,224,0,0],"FLAG":0,"BASE":18}
Panamalar NX-SM200 {"NAME":"NX-SM200","GPIO":[0,0,0,0,320,2720,0,0,2624,32,2656,224,0,4704],"FLAG":0,"BASE":18} Panamalar NX-SM200 {"NAME":"NX-SM200","GPIO":[0,0,0,0,320,2720,0,0,2624,32,2656,224,0,4704],"FLAG":0,"BASE":18}
Polycam Hohm Lanre 16A {"NAME":"SLV1910001","GPIO":[0,0,0,32,2720,2656,0,0,2624,576,224,0,0,0],"FLAG":0,"BASE":18}
Positivo PPW1000 {"NAME":"PPW1000","GPIO":[0,0,320,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45} Positivo PPW1000 {"NAME":"PPW1000","GPIO":[0,0,320,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45}
Positivo Max {"NAME":"PPW1600","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":55} Positivo Max {"NAME":"PPW1600","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":55}
PowerAdd BIE0091 {"NAME":"BIE0091","GPIO":[32,0,0,0,0,0,0,0,416,0,0,224,0,0],"FLAG":0,"BASE":18} PowerAdd BIE0091 {"NAME":"BIE0091","GPIO":[32,0,0,0,0,0,0,0,416,0,0,224,0,0],"FLAG":0,"BASE":18}
@ -1543,6 +1552,8 @@ Teckin SP27 {"NAME":"Teckin SP27","GPIO":[320,1,1,1,1,1,0,0,1,3
Tellur 16A 2 Ports {"NAME":"Tellur WiFi Smart Socket","GPIO":[0,0,0,2624,96,2688,0,0,224,33,2656,225,0,0],"FLAG":0,"BASE":18} Tellur 16A 2 Ports {"NAME":"Tellur WiFi Smart Socket","GPIO":[0,0,0,2624,96,2688,0,0,224,33,2656,225,0,0],"FLAG":0,"BASE":18}
Tellur 1USB 10A {"NAME":"Tellur TTL331021","GPIO":[0,0,544,0,288,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Tellur 1USB 10A {"NAME":"Tellur TTL331021","GPIO":[0,0,544,0,288,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Tflag NX-SM100 {"NAME":"NX-SM100","GPIO":[320,0,0,0,0,2720,0,0,224,32,2656,321,2624,0],"FLAG":0,"BASE":18} Tflag NX-SM100 {"NAME":"NX-SM100","GPIO":[320,0,0,0,0,2720,0,0,224,32,2656,321,2624,0],"FLAG":0,"BASE":18}
TH3D EZPlug V1 {"NAME":"EZPlug V1","GPIO":[0,0,0,32,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":1}
TH3D EZPlug+ V1 {"NAME":"EZPlug+ V1","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":1}
TikLok TL650 {"NAME":"TikLok Mini","GPIO":[0,0,0,0,321,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} TikLok TL650 {"NAME":"TikLok Mini","GPIO":[0,0,0,0,321,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Timethinker C338 {"NAME":"C338","GPIO":[32,0,1,0,0,0,0,0,224,288,1,0,0,0],"FLAG":0,"BASE":1} Timethinker C338 {"NAME":"C338","GPIO":[32,0,1,0,0,0,0,0,224,288,1,0,0,0],"FLAG":0,"BASE":1}
Timethinker TK04 {"NAME":"TimethinkerEU","GPIO":[1,1,1,1,32,1,0,0,1,288,224,1,0,0],"FLAG":0,"BASE":18} Timethinker TK04 {"NAME":"TimethinkerEU","GPIO":[1,1,1,1,32,1,0,0,1,288,224,1,0,0],"FLAG":0,"BASE":18}
@ -1562,6 +1573,7 @@ Treatlife Dimmable {"NAME":"DP20","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,
Treatlife Smart {"NAME":"Treatlife SK50","GPIO":[1,1,1,1,320,576,1,1,224,1,32,1,1,1],"FLAG":0,"BASE":18} Treatlife Smart {"NAME":"Treatlife SK50","GPIO":[1,1,1,1,320,576,1,1,224,1,32,1,1,1],"FLAG":0,"BASE":18}
Tuya 16A Nightlight {"NAME":"Nightlight","GPIO":[225,0,320,0,226,227,0,0,34,64,0,224,0,0],"FLAG":0,"BASE":18} Tuya 16A Nightlight {"NAME":"Nightlight","GPIO":[225,0,320,0,226,227,0,0,34,64,0,224,0,0],"FLAG":0,"BASE":18}
U10 Series {"NAME":"WIFI-Socket","GPIO":[1,32,1,1,1,1,1,1,1,320,224,1,1,4704],"FLAG":0,"BASE":18} U10 Series {"NAME":"WIFI-Socket","GPIO":[1,32,1,1,1,1,1,1,1,320,224,1,1,4704],"FLAG":0,"BASE":18}
Ucomen Night Light {"NAME":"UCOMEN Plug","GPIO":[0,0,0,0,544,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
UltraBrite {"NAME":"UltraBrite Smart Plug","GPIO":[1,1,1,1,288,289,1,1,224,32,1,1,1,1],"FLAG":0,"BASE":18} UltraBrite {"NAME":"UltraBrite Smart Plug","GPIO":[1,1,1,1,288,289,1,1,224,32,1,1,1,1],"FLAG":0,"BASE":18}
Ultralink UL-P01W {"NAME":"UL-P01W","GPIO":[0,288,0,32,2720,2656,0,0,2624,544,224,0,0,0],"FLAG":0,"BASE":18} Ultralink UL-P01W {"NAME":"UL-P01W","GPIO":[0,288,0,32,2720,2656,0,0,2624,544,224,0,0,0],"FLAG":0,"BASE":18}
Unlocked Automation 15A {"NAME":"UA 100","GPIO":[0,0,0,0,320,321,0,0,224,32,0,0,0,1],"FLAG":0,"BASE":18} Unlocked Automation 15A {"NAME":"UA 100","GPIO":[0,0,0,0,320,321,0,0,224,32,0,0,0,1],"FLAG":0,"BASE":18}
@ -1613,7 +1625,7 @@ XS-A18 {"NAME":"XS-A18","GPIO":[416,0,417,0,0,418,0,0,0,32
XS-A23 {"NAME":"XS-A23","GPIO":[320,1,0,2624,32,2720,0,0,0,33,2656,224,225,0],"FLAG":0,"BASE":45} XS-A23 {"NAME":"XS-A23","GPIO":[320,1,0,2624,32,2720,0,0,0,33,2656,224,225,0],"FLAG":0,"BASE":45}
XS-SSA01 {"NAME":"XS-SSA01","GPIO":[1,0,0,1,0,0,0,0,320,32,1,224,1,0],"FLAG":0,"BASE":18} XS-SSA01 {"NAME":"XS-SSA01","GPIO":[1,0,0,1,0,0,0,0,320,32,1,224,1,0],"FLAG":0,"BASE":18}
XS-SSA01 v2 {"NAME":"XS-SSA01","GPIO":[1,32,1,1,1,1,0,0,320,1,1,224,1,0],"FLAG":0,"BASE":18} XS-SSA01 v2 {"NAME":"XS-SSA01","GPIO":[1,32,1,1,1,1,0,0,320,1,1,224,1,0],"FLAG":0,"BASE":18}
XS-SSA05 {"NAME":"XS-SSA05","GPIO":[257,1,1,2624,1,2688,0,0,224,32,2656,258,1,4704],"FLAG":0,"BASE":18} XS-SSA05 {"NAME":"XS-SSA05","GPIO":[320,0,0,2624,544,2688,0,0,224,32,2656,0,0,4704],"FLAG":0,"BASE":18}
XS-SSA06 {"NAME":"XS-SSA06","GPIO":[416,0,417,0,0,418,0,0,0,64,0,224,0,0],"FLAG":0,"BASE":18} XS-SSA06 {"NAME":"XS-SSA06","GPIO":[416,0,417,0,0,418,0,0,0,64,0,224,0,0],"FLAG":0,"BASE":18}
Yagala SWA9 {"NAME":"SWA9","GPIO":[0,0,0,0,288,224,0,0,0,32,0,0,0,0],"FLAG":0,"BASE":18} Yagala SWA9 {"NAME":"SWA9","GPIO":[0,0,0,0,288,224,0,0,0,32,0,0,0,0],"FLAG":0,"BASE":18}
Yelomin JH-G01E {"NAME":"Yelomin","GPIO":[0,3072,0,3104,0,0,0,0,32,320,224,0,0,0],"FLAG":0,"BASE":18} Yelomin JH-G01E {"NAME":"Yelomin","GPIO":[0,3072,0,3104,0,0,0,0,32,320,224,0,0,0],"FLAG":0,"BASE":18}
@ -1706,12 +1718,13 @@ Konesky Type 1 {"NAME":"Konesky","GPIO":[0,0,0,0,228,225,0,0,227,3
Koogeek KLOE4 {"NAME":"Koogeek KLOE4","GPIO":[0,320,0,32,225,224,0,0,226,227,228,0,0,4704],"FLAG":0,"BASE":18} Koogeek KLOE4 {"NAME":"Koogeek KLOE4","GPIO":[0,320,0,32,225,224,0,0,226,227,228,0,0,4704],"FLAG":0,"BASE":18}
Larkkey 4AC 4USB {"NAME":"LARKKEY Strip","GPIO":[0,544,0,32,225,224,0,0,226,227,228,0,0,0],"FLAG":0,"BASE":18} Larkkey 4AC 4USB {"NAME":"LARKKEY Strip","GPIO":[0,544,0,32,225,224,0,0,226,227,228,0,0,0],"FLAG":0,"BASE":18}
LeFun SK2 {"NAME":"LeFun SK2","GPIO":[0,0,0,32,225,224,0,0,226,227,228,0,0,0],"FLAG":0,"BASE":18} LeFun SK2 {"NAME":"LeFun SK2","GPIO":[0,0,0,32,225,224,0,0,226,227,228,0,0,0],"FLAG":0,"BASE":18}
Lidl Silvercrest Zigbee {"NAME":"Lidl Silvercrest HG06338","GPIO":[0,288,0,0,225,226,0,0,32,224,576,0,0,0],"FLAG":0,"BASE":18}
LITEdge Smart Power Strip {"NAME":"LITEEdge Power Strip","GPIO":[227,0,0,0,288,289,0,0,224,32,225,226,228,0],"FLAG":0,"BASE":18} LITEdge Smart Power Strip {"NAME":"LITEEdge Power Strip","GPIO":[227,0,0,0,288,289,0,0,224,32,225,226,228,0],"FLAG":0,"BASE":18}
Luminea 3AC+4USB 16A {"NAME":"Luminea-NX4473","GPIO":[0,320,0,32,225,224,0,0,0,226,227,0,0,0],"FLAG":0,"BASE":18} Luminea 3AC+4USB 16A {"NAME":"Luminea-NX4473","GPIO":[0,320,0,32,225,224,0,0,0,226,227,0,0,0],"FLAG":0,"BASE":18}
Maxcio ZLD-34EU-W {"NAME":"MAXCIO","GPIO":[0,320,0,32,225,224,0,0,0,226,227,0,0,4704],"FLAG":0,"BASE":18} Maxcio ZLD-34EU-W {"NAME":"MAXCIO","GPIO":[0,320,0,32,225,224,0,0,0,226,227,0,0,4704],"FLAG":0,"BASE":18}
Merkury Innovations Smart Surge {"NAME":"Merkury Power Strip MIC-SW002-199L","GPIO":[288,0,289,0,228,32,0,0,225,224,226,0,259,0],"FLAG":0,"BASE":18} Merkury Innovations Smart Surge {"NAME":"Merkury Power Strip MIC-SW002-199L","GPIO":[288,0,289,0,228,32,0,0,225,224,226,0,259,0],"FLAG":0,"BASE":18}
Merkury Innovations SmartSurge {"NAME":"Merkury MI-SW001","GPIO":[288,0,289,0,228,32,0,0,225,224,226,0,227,0],"FLAG":0,"BASE":18} Merkury Innovations SmartSurge {"NAME":"Merkury MI-SW001","GPIO":[288,0,289,0,228,32,0,0,225,224,226,0,227,0],"FLAG":0,"BASE":18}
Meross 4AC 4USB {"NAME":"HamaStrip","GPIO":[0,544,0,32,225,224,0,0,226,227,228,0,0,0],"FLAG":0,"BASE":18} Meross 4AC 4USB {"NAME":"MSS425F","GPIO":[0,544,0,32,225,224,0,0,226,227,260,0,0,0],"FLAG":0,"BASE":18}
Meross MSS425 {"NAME":"Meross MSS425","GPIO":[260,0,0,0,320,0,0,0,224,32,225,226,259,0],"FLAG":0,"BASE":18} Meross MSS425 {"NAME":"Meross MSS425","GPIO":[260,0,0,0,320,0,0,0,224,32,225,226,259,0],"FLAG":0,"BASE":18}
Mirabella Genio 4 Outlet Power Board with 2 USB {"NAME":"Genio i002340","GPIO":[320,0,0,0,224,225,0,0,226,32,227,228,0,0],"FLAG":0,"BASE":18} Mirabella Genio 4 Outlet Power Board with 2 USB {"NAME":"Genio i002340","GPIO":[320,0,0,0,224,225,0,0,226,32,227,228,0,0],"FLAG":0,"BASE":18}
Mirabella Genio Powerboard {"NAME":"Genio Powerboa","GPIO":[224,288,0,0,226,225,0,0,228,32,229,227,0,0],"FLAG":0,"BASE":18} Mirabella Genio Powerboard {"NAME":"Genio Powerboa","GPIO":[224,288,0,0,226,225,0,0,228,32,229,227,0,0],"FLAG":0,"BASE":18}
@ -1742,6 +1755,7 @@ Tellur 3AC 4USB {"NAME":"Tellur","GPIO":[0,320,0,32,225,224,0,0,0,2
Tessan {"NAME":"TESSAN A4L-BK","GPIO":[0,0,0,227,226,0,0,0,224,0,225,0,0,0],"FLAG":0,"BASE":18} Tessan {"NAME":"TESSAN A4L-BK","GPIO":[0,0,0,227,226,0,0,0,224,0,225,0,0,0],"FLAG":0,"BASE":18}
Tonbux SM-SO301-U {"NAME":"Tonbux SM-SO30","GPIO":[320,0,0,0,256,0,0,0,258,257,259,0,228,0],"FLAG":0,"BASE":18} Tonbux SM-SO301-U {"NAME":"Tonbux SM-SO30","GPIO":[320,0,0,0,256,0,0,0,258,257,259,0,228,0],"FLAG":0,"BASE":18}
Useelink {"NAME":"Useelink","GPIO":[288,0,0,321,256,32,0,0,258,257,259,0,228,0],"FLAG":0,"BASE":18} Useelink {"NAME":"Useelink","GPIO":[288,0,0,321,256,32,0,0,258,257,259,0,228,0],"FLAG":0,"BASE":18}
Useelink 4AC 2USB {"NAME":"306 Power Strip","GPIO":[576,0,576,291,259,32,0,0,257,258,256,0,228,0],"FLAG":0,"BASE":18}
Vivitar HA-1007 {"NAME":"Vivitar HA-1007 Power Strip","GPIO":[544,0,0,0,227,228,0,0,225,224,226,0,35,1],"FLAG":0,"BASE":18} Vivitar HA-1007 {"NAME":"Vivitar HA-1007 Power Strip","GPIO":[544,0,0,0,227,228,0,0,225,224,226,0,35,1],"FLAG":0,"BASE":18}
Vivitar HA-1007-AU {"NAME":"HA-1007-AU","GPIO":[320,32,0,322,256,321,0,0,258,257,259,0,228,0],"FLAG":0,"BASE":18} Vivitar HA-1007-AU {"NAME":"HA-1007-AU","GPIO":[320,32,0,322,256,321,0,0,258,257,259,0,228,0],"FLAG":0,"BASE":18}
wesmartify essentials 3-socket 2 USB {"NAME":"Essentials Smart Home 3-socket USB Power Strip","GPIO":[0,0,0,0,544,226,0,0,224,32,225,0,0,0],"FLAG":0,"BASE":18} wesmartify essentials 3-socket 2 USB {"NAME":"Essentials Smart Home 3-socket USB Power Strip","GPIO":[0,0,0,0,544,226,0,0,224,32,225,0,0,0],"FLAG":0,"BASE":18}
@ -1883,6 +1897,8 @@ iQtech 9W 800lm {"NAME":"iQ-Tech RGBCCT 9W 800LM","GPIO":[0,0,0,0,4
Jeeo TF-QPZ13 800lm {"NAME":"Jeeo","GPIO":[0,0,0,0,2912,416,0,0,417,2976,2944,0,0,0],"FLAG":0,"BASE":18} Jeeo TF-QPZ13 800lm {"NAME":"Jeeo","GPIO":[0,0,0,0,2912,416,0,0,417,2976,2944,0,0,0],"FLAG":0,"BASE":18}
Julun JL-021 5W Candle {"NAME":"E14 RGBCCT","GPIO":[0,0,0,0,419,420,0,0,417,418,416,0,0,0],"FLAG":0,"BASE":18} Julun JL-021 5W Candle {"NAME":"E14 RGBCCT","GPIO":[0,0,0,0,419,420,0,0,417,418,416,0,0,0],"FLAG":0,"BASE":18}
Kauf esphome 10W {"NAME":"Kauf Bulb","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} Kauf esphome 10W {"NAME":"Kauf Bulb","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18}
Kauf esphome A15 5W {"NAME":"Kauf Bulb","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18}
Kauf esphome A19 7W {"NAME":"Kauf Bulb","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18}
KHSUIN BR30 13W 1300lm {"NAME":"KHSUIN 13W BR30","GPIO":[0,0,0,0,416,420,0,0,417,419,418,0,0,0],"FLAG":0,"BASE":18} KHSUIN BR30 13W 1300lm {"NAME":"KHSUIN 13W BR30","GPIO":[0,0,0,0,416,420,0,0,417,419,418,0,0,0],"FLAG":0,"BASE":18}
Kogan 10W 1050lm {"NAME":"Kogan RGB+CCT","GPIO":[1,1,1,0,416,419,1,1,417,452,418,1,1,1],"FLAG":0,"BASE":18} Kogan 10W 1050lm {"NAME":"Kogan RGB+CCT","GPIO":[1,1,1,0,416,419,1,1,417,452,418,1,1,1],"FLAG":0,"BASE":18}
Kohree 600lm {"NAME":"Kohree VHP560","GPIO":[0,0,0,0,416,420,0,0,417,419,418,0,0,0],"FLAG":0,"BASE":18} Kohree 600lm {"NAME":"Kohree VHP560","GPIO":[0,0,0,0,416,420,0,0,417,419,418,0,0,0],"FLAG":0,"BASE":18}
@ -2133,6 +2149,7 @@ Nedis 6W 470lm {"NAME":"nedis Bulb","GPIO":[0,0,0,0,416,419,0,0,41
Nedis A60 800lm {"NAME":"Nedis RGBW","GPIO":[0,0,0,0,2912,416,0,0,0,2976,2944,0,0,4704],"FLAG":0,"BASE":18} Nedis A60 800lm {"NAME":"Nedis RGBW","GPIO":[0,0,0,0,2912,416,0,0,0,2976,2944,0,0,4704],"FLAG":0,"BASE":18}
Nedis C10 350lm {"NAME":"Nedis WIFILC10","GPIO":[0,0,0,0,418,416,0,0,419,417,420,0,0,4704],"FLAG":0,"BASE":18} Nedis C10 350lm {"NAME":"Nedis WIFILC10","GPIO":[0,0,0,0,418,416,0,0,419,417,420,0,0,4704],"FLAG":0,"BASE":18}
Nedis PAR16 330lm {"NAME":"Nedis GU10","GPIO":[0,0,0,0,418,416,0,0,419,417,420,0,0,0],"FLAG":0,"BASE":18} Nedis PAR16 330lm {"NAME":"Nedis GU10","GPIO":[0,0,0,0,418,416,0,0,419,417,420,0,0,0],"FLAG":0,"BASE":18}
NGteco {"NAME":"NGTECO L100","GPIO":[0,0,0,0,0,418,0,0,417,0,416,419,0,0],"FLAG":0,"BASE":18}
Novostella UT55506 10W 1050lm {"NAME":"Novostella 10W","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} Novostella UT55506 10W 1050lm {"NAME":"Novostella 10W","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18}
Onforu 7W 700lm {"NAME":"Onforu RGBW","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} Onforu 7W 700lm {"NAME":"Onforu RGBW","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18}
Orbecco 5W 400lm {"NAME":"Orbecco Bulb","GPIO":[0,0,0,0,0,0,0,0,3008,0,3040,0,0,0],"FLAG":0,"BASE":27} Orbecco 5W 400lm {"NAME":"Orbecco Bulb","GPIO":[0,0,0,0,0,0,0,0,3008,0,3040,0,0,0],"FLAG":0,"BASE":27}
@ -2164,6 +2181,7 @@ Teckin 7.5W 800lm {"NAME":"Teckin SB60","GPIO":[0,0,0,0,416,419,0,0,4
Teckin SB50 800lm {"NAME":"Teckin SB50","GPIO":[0,0,0,0,419,0,0,0,417,418,416,0,0,0],"FLAG":0,"BASE":18} Teckin SB50 800lm {"NAME":"Teckin SB50","GPIO":[0,0,0,0,419,0,0,0,417,418,416,0,0,0],"FLAG":0,"BASE":18}
Teckin SB50 v2 800lm {"NAME":"Teckin SB50","GPIO":[0,0,0,0,416,0,0,0,417,419,418,0,0,0],"FLAG":0,"BASE":18} Teckin SB50 v2 800lm {"NAME":"Teckin SB50","GPIO":[0,0,0,0,416,0,0,0,417,419,418,0,0,0],"FLAG":0,"BASE":18}
Teckin SB51 800lm {"NAME":"Teckin SB51","GPIO":[0,0,0,0,419,0,0,0,417,418,416,0,0,0],"FLAG":0,"BASE":18} Teckin SB51 800lm {"NAME":"Teckin SB51","GPIO":[0,0,0,0,419,0,0,0,417,418,416,0,0,0],"FLAG":0,"BASE":18}
TH3D EZBulb V1 {"NAME":"EZBulb V1","GPIO":[0,0,0,0,416,419,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18}
TikLOk TL530 A19 7.5W 800lm {"NAME":"TikLOk WW-CW-L","GPIO":[0,0,0,0,0,416,0,0,417,0,0,0,0,0],"FLAG":0,"BASE":18} TikLOk TL530 A19 7.5W 800lm {"NAME":"TikLOk WW-CW-L","GPIO":[0,0,0,0,0,416,0,0,417,0,0,0,0,0],"FLAG":0,"BASE":18}
TVLive 7.5W 800lm {"NAME":"TVLIVE RGBCW","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} TVLive 7.5W 800lm {"NAME":"TVLIVE RGBCW","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18}
Utorch LE7 600lm {"NAME":"Utorch LE7","GPIO":[0,0,0,0,0,417,0,0,418,0,419,416,0,0],"FLAG":0,"BASE":18} Utorch LE7 600lm {"NAME":"Utorch LE7","GPIO":[0,0,0,0,0,417,0,0,418,0,419,416,0,0],"FLAG":0,"BASE":18}
@ -2573,9 +2591,9 @@ Sonoff TX T3 EU 2 Gang {"NAME":"Sonoff T3 TX 2CH","GPIO":[32,1,1,1,0,225,3
Sonoff TX T3 EU 3 Gang {"NAME":"TX T3EU3C","GPIO":[32,1,0,1,226,225,33,34,224,576,0,0,0,0],"FLAG":0,"BASE":30} Sonoff TX T3 EU 3 Gang {"NAME":"TX T3EU3C","GPIO":[32,1,0,1,226,225,33,34,224,576,0,0,0,0],"FLAG":0,"BASE":30}
Sonoff TX T3 US 3 Gang {"NAME":"TX T3US3C","GPIO":[32,1,0,1,226,225,33,34,224,576,0,0,0,0],"FLAG":0,"BASE":30} Sonoff TX T3 US 3 Gang {"NAME":"TX T3US3C","GPIO":[32,1,0,1,226,225,33,34,224,576,0,0,0,0],"FLAG":0,"BASE":30}
Sonoff TX T4 EU No Neutral 1 Gang {"NAME":"Sonoff T4 1CH","GPIO":[32,1,1,1,0,0,0,0,224,320,0,0,0,0],"FLAG":0,"BASE":28} Sonoff TX T4 EU No Neutral 1 Gang {"NAME":"Sonoff T4 1CH","GPIO":[32,1,1,1,0,0,0,0,224,320,0,0,0,0],"FLAG":0,"BASE":28}
Sonoff TX Ultimate 1 Gang {"NAME":"TX Ultimate 1","GPIO":[0,0,7808,0,7840,3872,0,0,0,1376,0,7776,0,0,224,3232,0,480,3200,0,0,0,3840,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"Pixels 27"} Sonoff TX Ultimate 1 Gang {"NAME":"TX Ultimate 1","GPIO":[0,0,7808,0,7840,3872,0,0,0,1376,0,7776,0,0,224,3232,0,480,3200,0,0,0,3840,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"Backlog Pixels 28"}
Sonoff TX Ultimate 2 Gang {"NAME":"TX Ultimate 1","GPIO":[0,0,7808,0,7840,3872,0,0,0,1376,0,7776,0,225,224,3232,0,480,3200,0,0,0,3840,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"Pixels 27"} Sonoff TX Ultimate 2 Gang {"NAME":"TX Ultimate 2","GPIO":[0,0,7808,0,7840,3872,0,0,0,1376,0,7776,0,225,224,3232,0,480,3200,0,0,0,3840,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"Backlog Pixels 28"}
Sonoff TX Ultimate 3 Gang {"NAME":"TX Ultimate 1","GPIO":[0,0,7808,0,7840,3872,0,0,0,1376,0,7776,0,225,224,3232,0,480,3200,0,0,0,3840,226,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"Pixels 27"} Sonoff TX Ultimate 3 Gang {"NAME":"TX Ultimate 3","GPIO":[0,0,7808,0,7840,3872,0,0,0,1376,0,7776,0,225,224,3232,0,480,3200,0,0,0,3840,226,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"Backlog Pixels 28"}
SPC Hera {"NAME":"SPC HERA","GPIO":[544,0,0,32,224,0,0,0,0,0,288,0,0,0],"FLAG":0,"BASE":18} SPC Hera {"NAME":"SPC HERA","GPIO":[544,0,0,32,224,0,0,0,0,0,288,0,0,0],"FLAG":0,"BASE":18}
SRL 2 Gang {"NAME":"SRL 4WW Switch","GPIO":[0,0,0,0,0,33,0,0,32,224,0,225,0,0],"FLAG":0,"BASE":18} SRL 2 Gang {"NAME":"SRL 4WW Switch","GPIO":[0,0,0,0,0,33,0,0,32,224,0,225,0,0],"FLAG":0,"BASE":18}
SRL 4 Gang {"NAME":"SRL 4WW Switch","GPIO":[0,0,0,34,226,33,0,0,32,224,227,225,35,0],"FLAG":0,"BASE":18} SRL 4 Gang {"NAME":"SRL 4WW Switch","GPIO":[0,0,0,34,226,33,0,0,32,224,227,225,35,0],"FLAG":0,"BASE":18}
@ -2687,7 +2705,9 @@ Moes {"NAME":"Moes MS-104B","GPIO":[0,0,32,0,480,0,0,0,1
Moes 10A {"NAME":"Moes MS-101","GPIO":[0,0,0,0,0,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Moes 10A {"NAME":"Moes MS-101","GPIO":[0,0,0,0,0,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Moes Mini 3 Gang 1/2 Way {"NAME":"Moes MS-104C","GPIO":[0,0,0,34,32,33,0,0,224,225,226,0,0,0],"FLAG":0,"BASE":18} Moes Mini 3 Gang 1/2 Way {"NAME":"Moes MS-104C","GPIO":[0,0,0,34,32,33,0,0,224,225,226,0,0,0],"FLAG":0,"BASE":18}
Nedis 10A {"NAME":"Nedis WIFIPS10WT","GPIO":[0,0,0,0,224,0,0,0,32,321,0,288,0,0],"FLAG":0,"BASE":18} Nedis 10A {"NAME":"Nedis WIFIPS10WT","GPIO":[0,0,0,0,224,0,0,0,32,321,0,288,0,0],"FLAG":0,"BASE":18}
Nous 1 Channel Touch {"NAME":"NOUS L1T","GPIO":[544,0,1,32,0,0,0,0,0,224,288,0,0,0],"FLAG":0,"BASE":1}
Nous 1/2 Channel {"NAME":"NOUS L13T Smart Switch Module","GPIO":[1,161,1,160,225,224,1,1,544,1,32,1,1,1],"FLAG":0,"BASE":18} Nous 1/2 Channel {"NAME":"NOUS L13T Smart Switch Module","GPIO":[1,161,1,160,225,224,1,1,544,1,32,1,1,1],"FLAG":0,"BASE":18}
Nous 2 Channel Touch {"NAME":"NOUS L2T","GPIO":[544,289,1,32,225,33,0,0,0,224,288,0,0,0],"FLAG":0,"BASE":1}
Nova Digital Basic 1 MS101 {"NAME":"NovaDigBasic1","GPIO":[0,1,0,1,320,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Nova Digital Basic 1 MS101 {"NAME":"NovaDigBasic1","GPIO":[0,1,0,1,320,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
PPA Contatto Wi-Fi {"NAME":"PPA Contatto","GPIO":[0,0,32,0,224,162,0,0,288,225,0,0,0,0],"FLAG":0,"BASE":18} PPA Contatto Wi-Fi {"NAME":"PPA Contatto","GPIO":[0,0,32,0,224,162,0,0,288,225,0,0,0,0],"FLAG":0,"BASE":18}
PS-1604 16A {"NAME":"PS-1604 16A","GPIO":[32,1,1,1,1,0,0,0,224,320,1,0,0,0],"FLAG":0,"BASE":1} PS-1604 16A {"NAME":"PS-1604 16A","GPIO":[32,1,1,1,1,0,0,0,224,320,1,0,0,0],"FLAG":0,"BASE":1}
@ -2786,6 +2806,7 @@ Tuya Gas/Water {"NAME":"Valve FM101","GPIO":[320,0,0,0,224,0,0,0,0
Aigostar P40 {"NAME":"Aigostar 8433325212278","GPIO":[0,0,0,0,544,288,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Aigostar P40 {"NAME":"Aigostar 8433325212278","GPIO":[0,0,0,0,544,288,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Aseer THWFS01 {"NAME":"ASEER-THWFS01","GPIO":[320,33,544,323,2720,2656,0,0,2624,225,321,224,32,0],"FLAG":0,"BASE":18} Aseer THWFS01 {"NAME":"ASEER-THWFS01","GPIO":[320,33,544,323,2720,2656,0,0,2624,225,321,224,32,0],"FLAG":0,"BASE":18}
Athom {"NAME":"Athom SK01","GPIO":[0,0,0,3104,0,32,0,0,224,320,0,0,0,0],"FLAG":0,"BASE":18} Athom {"NAME":"Athom SK01","GPIO":[0,0,0,3104,0,32,0,0,224,320,0,0,0,0],"FLAG":0,"BASE":18}
Athom 16A UK {"NAME":"Athom SK03-TAS","GPIO":[0,0,0,3104,0,32,0,0,224,576,0,0,0,0],"FLAG":0,"BASE":18}
Bestten LO-2-W {"NAME":"BESTTEN LO-2-W","GPIO":[0,0,0,0,576,32,0,0,224,0,0,0,0,0],"FLAG":0,"BASE":18} Bestten LO-2-W {"NAME":"BESTTEN LO-2-W","GPIO":[0,0,0,0,576,32,0,0,224,0,0,0,0,0],"FLAG":0,"BASE":18}
BingoElec 16A {"NAME":"BingoElecPower","GPIO":[0,0,0,0,288,289,1,1,224,32,0,0,1,1],"FLAG":0,"BASE":18} BingoElec 16A {"NAME":"BingoElecPower","GPIO":[0,0,0,0,288,289,1,1,224,32,0,0,1,1],"FLAG":0,"BASE":18}
BlitzWolf SHP8 {"NAME":"SHP8","GPIO":[0,320,0,32,2720,2656,0,0,2624,289,224,0,0,0],"FLAG":0,"BASE":64} BlitzWolf SHP8 {"NAME":"SHP8","GPIO":[0,320,0,32,2720,2656,0,0,2624,289,224,0,0,0],"FLAG":0,"BASE":64}
@ -2816,6 +2837,7 @@ Milfra UK Double USB Chager Twin {"NAME":"Milfra TBU02","GPIO":[0,0,0,0,288,33,
Moes 16A {"NAME":"WK-EU(FR/UK)16M","GPIO":[0,288,0,32,2720,2656,0,0,2624,224,0,0,0,0],"FLAG":0,"BASE":18} Moes 16A {"NAME":"WK-EU(FR/UK)16M","GPIO":[0,288,0,32,2720,2656,0,0,2624,224,0,0,0,0],"FLAG":0,"BASE":18}
Moes WWK Glass Panel {"NAME":"Smart Socket","GPIO":[0,0,0,0,288,289,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Moes WWK Glass Panel {"NAME":"Smart Socket","GPIO":[0,0,0,0,288,289,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18}
Oittm 120 {"NAME":"Oittm WS01","GPIO":[32,0,0,0,0,2592,0,0,224,2656,2688,288,0,0],"FLAG":0,"BASE":18} Oittm 120 {"NAME":"Oittm WS01","GPIO":[32,0,0,0,0,2592,0,0,224,2656,2688,288,0,0],"FLAG":0,"BASE":18}
PFS Presa Smart {"NAME":"PFS_PresaSmart","GPIO":[1,1,1,1,288,289,1,1,224,32,0,1,1,1],"FLAG":0,"BASE":18}
PS-1607 {"NAME":"PS-1607","GPIO":[32,0,0,0,0,225,33,0,224,0,0,0,0,0],"FLAG":0,"BASE":18} PS-1607 {"NAME":"PS-1607","GPIO":[32,0,0,0,0,225,33,0,224,0,0,0,0,0],"FLAG":0,"BASE":18}
Smanergy KA10 {"NAME":"KA10","GPIO":[0,320,0,32,2720,2656,0,0,2624,289,224,0,0,0],"FLAG":0,"BASE":64} Smanergy KA10 {"NAME":"KA10","GPIO":[0,320,0,32,2720,2656,0,0,2624,289,224,0,0,0],"FLAG":0,"BASE":64}
Sonoff IW100 {"NAME":"Sonoff IW100","GPIO":[32,3072,0,3104,0,0,0,0,224,544,0,0,0,0],"FLAG":0,"BASE":41} Sonoff IW100 {"NAME":"Sonoff IW100","GPIO":[32,3072,0,3104,0,0,0,0,224,544,0,0,0,0],"FLAG":0,"BASE":41}

44
boards/esp32c6.json Normal file
View File

@ -0,0 +1,44 @@
{
"build": {
"arduino":{
"ldscript": "esp32c6_out.ld"
},
"core": "esp32",
"extra_flags": "-DESP32_4M -DESP32C6",
"f_cpu": "160000000L",
"f_flash": "80000000L",
"flash_mode": "dio",
"mcu": "esp32c6",
"variant": "esp32c6",
"partitions": "partitions/esp32_partition_app2880k_fs320k.csv"
},
"connectivity": [
"wifi",
"bluetooth"
],
"debug": {
"openocd_target": "esp32c6.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "Espressif Generic ESP32-C6 >= 4M Flash, Tasmota 2880k Code/OTA, 320k FS",
"upload": {
"arduino": {
"flash_extra_images": [
[
"0x10000",
"variants/tasmota/tasmota32c6-safeboot.bin"
]
]
},
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.espressif.com/projects/espressif-esp-dev-kits/en/latest/esp32c6/esp32-c6-devkitc-1/index.html",
"vendor": "Espressif"
}

View File

@ -39,6 +39,8 @@ TasmotaSerial *tms_obj_list[16];
#ifdef ESP32 #ifdef ESP32
#include "driver/uart.h" #include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_rom_gpio.h"
static uint32_t tasmota_serial_uart_bitmap = 0; // Assigned UARTs static uint32_t tasmota_serial_uart_bitmap = 0; // Assigned UARTs
@ -466,6 +468,7 @@ size_t TasmotaSerial::write(uint8_t b) {
return size; return size;
} }
#ifdef ESP8266
void IRAM_ATTR TasmotaSerial::rxRead(void) { void IRAM_ATTR TasmotaSerial::rxRead(void) {
if (!m_nwmode) { if (!m_nwmode) {
uint32_t start = ESP.getCycleCount(); uint32_t start = ESP.getCycleCount();
@ -586,3 +589,4 @@ void IRAM_ATTR TasmotaSerial::rxRead(void) {
} }
} }
} }
#endif // ESP8266

View File

@ -10,8 +10,8 @@
This library enables you to **send _and_ receive** infra-red signals on an [ESP8266](https://github.com/esp8266/Arduino) or an This library enables you to **send _and_ receive** infra-red signals on an [ESP8266](https://github.com/esp8266/Arduino) or an
[ESP32](https://github.com/espressif/arduino-esp32) using the [Arduino framework](https://www.arduino.cc/) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* demodulators etc. [ESP32](https://github.com/espressif/arduino-esp32) using the [Arduino framework](https://www.arduino.cc/) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* demodulators etc.
## v2.8.5 Now Available ## v2.8.6 Now Available
Version 2.8.5 of the library is now [available](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. Version 2.8.6 of the library is now [available](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes.
#### Upgrading from pre-v2.0 #### Upgrading from pre-v2.0
Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page.

View File

@ -11,8 +11,8 @@
Diese Programmbibliothek ermöglicht das **Senden _und_ Empfangen** von Infrarotsignalen mit [ESP8266](https://github.com/esp8266/Arduino)- und Diese Programmbibliothek ermöglicht das **Senden _und_ Empfangen** von Infrarotsignalen mit [ESP8266](https://github.com/esp8266/Arduino)- und
[ESP32](https://github.com/espressif/arduino-esp32)-Mikrocontrollern mithilfe des [Arduino-Frameworks](https://www.arduino.cc/) und handelsüblichen 940nm Infrarot-LEDs undIR-Empfängermodulen, wie zum Beispiel TSOP{17,22,24,36,38,44,48}*-Demodulatoren. [ESP32](https://github.com/espressif/arduino-esp32)-Mikrocontrollern mithilfe des [Arduino-Frameworks](https://www.arduino.cc/) und handelsüblichen 940nm Infrarot-LEDs undIR-Empfängermodulen, wie zum Beispiel TSOP{17,22,24,36,38,44,48}*-Demodulatoren.
## v2.8.5 jetzt verfügbar ## v2.8.6 jetzt verfügbar
Version 2.8.5 der Bibliothek ist nun [verfügbar](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Die [Versionshinweise](ReleaseNotes.md) enthalten alle wichtigen Neuerungen. Version 2.8.6 der Bibliothek ist nun [verfügbar](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Die [Versionshinweise](ReleaseNotes.md) enthalten alle wichtigen Neuerungen.
#### Hinweis für Nutzer von Versionen vor v2.0 #### Hinweis für Nutzer von Versionen vor v2.0
Die Benutzung der Bibliothek hat sich mit Version 2.0 leicht geändert. Einige Anpassungen im aufrufenden Code werden nötig sein, um mit Version ab 2.0 korrekt zu funktionieren. Mehr zu den Anpassungen finden sich auf unserer [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0)-Seite. Die Benutzung der Bibliothek hat sich mit Version 2.0 leicht geändert. Einige Anpassungen im aufrufenden Code werden nötig sein, um mit Version ab 2.0 korrekt zu funktionieren. Mehr zu den Anpassungen finden sich auf unserer [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0)-Seite.

View File

@ -10,8 +10,8 @@
Cette librairie vous permetra de **recevoir et d'envoyer des signaux** infrarouge sur le protocole [ESP8266](https://github.com/esp8266/Arduino) ou sur le protocole Cette librairie vous permetra de **recevoir et d'envoyer des signaux** infrarouge sur le protocole [ESP8266](https://github.com/esp8266/Arduino) ou sur le protocole
[ESP32](https://github.com/espressif/arduino-esp32) en utilisant le [Arduino framework](https://www.arduino.cc/) qui utilise la norme 940nm IR LEDs et le module basique de reception d'onde IR. Exemple : TSOP{17,22,24,36,38,44,48}* modules etc. [ESP32](https://github.com/espressif/arduino-esp32) en utilisant le [Arduino framework](https://www.arduino.cc/) qui utilise la norme 940nm IR LEDs et le module basique de reception d'onde IR. Exemple : TSOP{17,22,24,36,38,44,48}* modules etc.
## v2.8.5 disponible ## v2.8.6 disponible
Version 2.8.5 de la libraire est maintenant [disponible](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Vous pouvez voir le [Release Notes](ReleaseNotes.md) pour tous les changements importants. Version 2.8.6 de la libraire est maintenant [disponible](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Vous pouvez voir le [Release Notes](ReleaseNotes.md) pour tous les changements importants.
#### mise à jour depuis pre-v2.0 #### mise à jour depuis pre-v2.0
L'utilisation de la librairie à un peu changer depuis la version in v2.0. Si vous voulez l'utiliser vous devrez changer votre utilisation aussi. Vous pouvez vous renseigner sur les précondition d'utilisation ici : [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. L'utilisation de la librairie à un peu changer depuis la version in v2.0. Si vous voulez l'utiliser vous devrez changer votre utilisation aussi. Vous pouvez vous renseigner sur les précondition d'utilisation ici : [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page.

View File

@ -10,8 +10,8 @@
Deze library maakt het mogelijk om Infraroodsignalen **te versturen en ontvangen** via het [Arduino framework](https://www.arduino.cc/) met veelgebruikte 940nm IR LEDs en IR ontvang modules. b.v. TSOP{17,22,24,36,38,44,48}* demodulatoren enz. Deze library maakt het mogelijk om Infraroodsignalen **te versturen en ontvangen** via het [Arduino framework](https://www.arduino.cc/) met veelgebruikte 940nm IR LEDs en IR ontvang modules. b.v. TSOP{17,22,24,36,38,44,48}* demodulatoren enz.
## v2.8.5 nu beschikbaar ## v2.8.6 nu beschikbaar
Versie 2.8.5 van de bibliotheek is nu [beschikbaar](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Bekijk de [Release Notes](ReleaseNotes.md) voor alle belangrijke veranderingen. Versie 2.8.6 van de bibliotheek is nu [beschikbaar](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Bekijk de [Release Notes](ReleaseNotes.md) voor alle belangrijke veranderingen.
#### Upgraden vanaf pre-v2.0 #### Upgraden vanaf pre-v2.0
Het gebruik van de bibliotheek is enigszins gewijzigd in v2.0. Je zult het gebruik moeten aanpassen om te kunnen werken met v2.0 en hoger. Je kunt meer lezen over de vereiste aanpassingen op onze [Upgraden naar v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) pagina. Het gebruik van de bibliotheek is enigszins gewijzigd in v2.0. Je zult het gebruik moeten aanpassen om te kunnen werken met v2.0 en hoger. Je kunt meer lezen over de vereiste aanpassingen op onze [Upgraden naar v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) pagina.

View File

@ -1,5 +1,23 @@
# Release Notes # Release Notes
## _v2.8.6 (20230727)_
**[Bug Fixes]**
- Ensure `IRCoolixAC::toCommon()` returns `kNoTempValue` when no sensor temp is detected. (#2015 #2012)
- Fix compilation dependency of LG on Samsung send protocol (#2011 #2010)
- Fix missing parameter in call to `IRac::gree()` (#2008 #2007)
**[Features]**
- IRac: Ensure the `sleep` parameter is used for the `FUJITSU_AC` protocol. (#1992 #1991)
**[Misc]**
- Allow the BlynkIRRemote.ino code to compile again. (#2016)
- do not list WHIRLPOOL_AC unconditionally as supported protocol (#2003)
- IRUtils:typeToString() — simplify (#2002)
- Fix brand Green -> Gree (#1994)
- Fix undefined `std::round` compilation error (#1989)
## _v2.8.5 (20230508)_ ## _v2.8.5 (20230508)_
**[Bug Fixes]** **[Bug Fixes]**

View File

@ -1,6 +1,6 @@
<!--- WARNING: Do NOT edit this file directly. <!--- WARNING: Do NOT edit this file directly.
It is generated by './tools/scrape_supported_devices.py'. It is generated by './tools/scrape_supported_devices.py'.
Last generated: Mon 08 May 2023 07:06:16 +0000 ---> Last generated: Thu 27 Jul 2023 05:37:11 +0000 --->
# IR Protocols supported by this library # IR Protocols supported by this library
| Protocol | Brand | Model | A/C Model | Detailed A/C Support | | Protocol | Brand | Model | A/C Model | Detailed A/C Support |
@ -65,8 +65,7 @@
| [JVC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **JVC** | PTU94023B remote | | - | | [JVC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **JVC** | PTU94023B remote | | - |
| [Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.cpp) | **[Hisense](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.h)** | AST-09UW4RVETG00A A/C (KELON168) | | Yes | | [Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.cpp) | **[Hisense](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.h)** | AST-09UW4RVETG00A A/C (KELON168) | | Yes |
| [Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.cpp) | **[Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.h)** | AST-09UW4RVETG00A A/C (KELON168)<BR>DG11R2-01 remote (KELON168)<BR>ON/OFF 9000-12000 (KELON) | | Yes | | [Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.cpp) | **[Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.h)** | AST-09UW4RVETG00A A/C (KELON168)<BR>DG11R2-01 remote (KELON168)<BR>ON/OFF 9000-12000 (KELON) | | Yes |
| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | YAP0F8 remote | | Yes | | [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | YAP0F8 remote<BR>YAPOF3 remote | | Yes |
| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Green](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | YAPOF3 remote | | Yes |
| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | KSV26CRC A/C<BR>KSV26HRC A/C<BR>KSV35CRC A/C<BR>KSV35HRC A/C<BR>KSV53HRC A/C<BR>KSV62HRC A/C<BR>KSV70CRC A/C<BR>KSV70HRC A/C<BR>KSV80HRC A/C<BR>YALIF Remote | | Yes | | [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | KSV26CRC A/C<BR>KSV26HRC A/C<BR>KSV35CRC A/C<BR>KSV35HRC A/C<BR>KSV53HRC A/C<BR>KSV62HRC A/C<BR>KSV70CRC A/C<BR>KSV70HRC A/C<BR>KSV80HRC A/C<BR>YALIF Remote | | Yes |
| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | A5VEY A/C<BR>YB1FA remote | | Yes | | [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | A5VEY A/C<BR>YB1FA remote | | Yes |
| [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[General Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711AR2853M Remote (LG - GE6711AR2853M)<BR>AG1BH09AW101 A/C (LG - GE6711AR2853M) | | Yes | | [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[General Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711AR2853M Remote (LG - GE6711AR2853M)<BR>AG1BH09AW101 A/C (LG - GE6711AR2853M) | | Yes |

View File

@ -104,6 +104,8 @@
/* Comment this out to disable prints and save space */ /* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial #define BLYNK_PRINT Serial
#define BLYNK_TEMPLATE_ID "TMPL••••••••" // Made up values. Please Change.
#define BLYNK_TEMPLATE_NAME "My First Device" // Please Change.
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>

View File

@ -64,4 +64,9 @@ build_flags = -D_IR_LOCALE_=zh-CN ; Chinese (Simplified)
; Build the library with all protocols disabled to flush out #if/#ifdef issues & ; Build the library with all protocols disabled to flush out #if/#ifdef issues &
; any compiler warnings, by turning them into errors. ; any compiler warnings, by turning them into errors.
[env:shakedown_no_protocols] [env:shakedown_no_protocols]
build_flags = -D_IR_ENABLE_DEFAULT_=false -Werror -Wno-error=switch build_flags =
${env.build_flags}
-Werror
-Wno-error=switch
-Wno-error=switch-unreachable
-D_IR_ENABLE_DEFAULT_=false

View File

@ -34,4 +34,5 @@ build_flags =
${env.build_flags} ${env.build_flags}
-Werror -Werror
-Wno-error=switch -Wno-error=switch
-Wno-error=switch-unreachable
-D_IR_ENABLE_DEFAULT_=false -D_IR_ENABLE_DEFAULT_=false

View File

@ -1,6 +1,6 @@
{ {
"name": "IRremoteESP8266", "name": "IRremoteESP8266",
"version": "2.8.5", "version": "2.8.6",
"keywords": "infrared, ir, remote, esp8266, esp32", "keywords": "infrared, ir, remote, esp8266, esp32",
"description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)", "description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)",
"repository": "repository":

View File

@ -1,5 +1,5 @@
name=IRremoteESP8266 name=IRremoteESP8266
version=2.8.5 version=2.8.6
author=David Conran, Sebastien Warin, Mark Szabo, Ken Shirriff author=David Conran, Sebastien Warin, Mark Szabo, Ken Shirriff
maintainer=David Conran, Mark Szabo, Sebastien Warin, Roi Dayan, Massimiliano Pinto, Christian Nilsson maintainer=David Conran, Mark Szabo, Sebastien Warin, Roi Dayan, Massimiliano Pinto, Christian Nilsson
sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32) sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32)

View File

@ -13,6 +13,11 @@
#include <string> #include <string>
#endif #endif
#include <cmath> #include <cmath>
#if __cplusplus >= 201103L && defined(_GLIBCXX_USE_C99_MATH_TR1)
using std::roundf;
#else
using ::roundf;
#endif
#include "IRsend.h" #include "IRsend.h"
#include "IRremoteESP8266.h" #include "IRremoteESP8266.h"
#include "IRtext.h" #include "IRtext.h"
@ -366,7 +371,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
#if SEND_YORK #if SEND_YORK
case decode_type_t::YORK: case decode_type_t::YORK:
#endif #endif
#if SEND_WHIRLPOOL_AC
case decode_type_t::WHIRLPOOL_AC: case decode_type_t::WHIRLPOOL_AC:
#endif
return true; return true;
default: default:
return false; return false;
@ -489,9 +496,9 @@ void IRac::argo(IRArgoAC *ac,
ac->begin(); ac->begin();
ac->setPower(on); ac->setPower(on);
ac->setMode(ac->convertMode(mode)); ac->setMode(ac->convertMode(mode));
ac->setTemp(static_cast<uint8_t>(std::round(degrees))); ac->setTemp(static_cast<uint8_t>(roundf(degrees)));
if (sensorTemp != kNoTempValue) { if (sensorTemp != kNoTempValue) {
ac->setSensorTemp(static_cast<uint8_t>(std::round(sensorTemp))); ac->setSensorTemp(static_cast<uint8_t>(roundf(sensorTemp)));
} }
ac->setiFeel(iFeel); ac->setiFeel(iFeel);
ac->setFan(ac->convertFan(fan)); ac->setFan(ac->convertFan(fan));
@ -537,7 +544,7 @@ void IRac::argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, const bool on,
ac->setMode(ac->convertMode(mode)); ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees); ac->setTemp(degrees);
if (sensorTemp != kNoTempValue) { if (sensorTemp != kNoTempValue) {
ac->setSensorTemp(static_cast<uint8_t>(std::round(sensorTemp))); ac->setSensorTemp(static_cast<uint8_t>(roundf(sensorTemp)));
} }
ac->setiFeel(iFeel); ac->setiFeel(iFeel);
ac->setFan(ac->convertFan(fan)); ac->setFan(ac->convertFan(fan));
@ -563,7 +570,7 @@ void IRac::argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, const bool on,
void IRac::argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp) { void IRac::argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp) {
ac->begin(); ac->begin();
ac->setMessageType(argoIrMessageType_t::IFEEL_TEMP_REPORT); ac->setMessageType(argoIrMessageType_t::IFEEL_TEMP_REPORT);
ac->setSensorTemp(static_cast<uint8_t>(std::round(sensorTemp))); ac->setSensorTemp(static_cast<uint8_t>(roundf(sensorTemp)));
ac->send(); ac->send();
} }
@ -738,7 +745,7 @@ void IRac::coolix(IRCoolixAC *ac,
// No Econo setting available. // No Econo setting available.
// No Quiet setting available. // No Quiet setting available.
if (sensorTemp != kNoTempValue) { if (sensorTemp != kNoTempValue) {
ac->setSensorTemp(static_cast<uint8_t>(std::round(sensorTemp))); ac->setSensorTemp(static_cast<uint8_t>(roundf(sensorTemp)));
} else { } else {
ac->clearSensorTemp(); ac->clearSensorTemp();
} }
@ -1128,7 +1135,7 @@ void IRac::ecoclim(IREcoclimAc *ac,
ac->setTemp(degrees); ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan)); ac->setFan(ac->convertFan(fan));
if (sensorTemp != kNoTempValue) { if (sensorTemp != kNoTempValue) {
ac->setSensorTemp(static_cast<uint8_t>(std::round(sensorTemp))); ac->setSensorTemp(static_cast<uint8_t>(roundf(sensorTemp)));
} else { } else {
ac->setSensorTemp(degrees); //< Set to the desired temp ac->setSensorTemp(degrees); //< Set to the desired temp
// until we can disable. // until we can disable.
@ -1174,7 +1181,7 @@ void IRac::electra(IRElectraAc *ac,
ac->setMode(ac->convertMode(mode)); ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees); ac->setTemp(degrees);
if (sensorTemp != kNoTempValue) { if (sensorTemp != kNoTempValue) {
ac->setSensorTemp(static_cast<uint8_t>(std::round(sensorTemp))); ac->setSensorTemp(static_cast<uint8_t>(roundf(sensorTemp)));
} }
ac->setFan(ac->convertFan(fan)); ac->setFan(ac->convertFan(fan));
ac->setSwingV(swingv != stdAc::swingv_t::kOff); ac->setSwingV(swingv != stdAc::swingv_t::kOff);
@ -2288,7 +2295,7 @@ void IRac::sanyo(IRSanyoAc *ac,
ac->setMode(ac->convertMode(mode)); ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees); ac->setTemp(degrees);
if (sensorTemp != kNoTempValue) { if (sensorTemp != kNoTempValue) {
ac->setSensorTemp(static_cast<uint8_t>(std::round(sensorTemp))); ac->setSensorTemp(static_cast<uint8_t>(roundf(sensorTemp)));
} else { } else {
ac->setSensorTemp(degrees); // Set the sensor temp to the desired ac->setSensorTemp(degrees); // Set the sensor temp to the desired
// (normal) temp. // (normal) temp.
@ -3229,7 +3236,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
fujitsu(&ac, (fujitsu_ac_remote_model_t)send.model, send.power, send.mode, fujitsu(&ac, (fujitsu_ac_remote_model_t)send.model, send.power, send.mode,
send.celsius, send.degrees, send.fanspeed, send.celsius, send.degrees, send.fanspeed,
send.swingv, send.swingh, send.quiet, send.swingv, send.swingh, send.quiet,
send.turbo, send.econo, send.filter, send.clean); send.turbo, send.econo, send.filter, send.clean, send.sleep);
break; break;
} }
#endif // SEND_FUJITSU_AC #endif // SEND_FUJITSU_AC
@ -3249,7 +3256,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
_modulation); _modulation);
gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode, gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode,
send.celsius, send.degrees, send.fanspeed, send.swingv, send.swingh, send.celsius, send.degrees, send.fanspeed, send.swingv, send.swingh,
send.turbo, send.econo, send.light, send.clean, send.sleep); send.iFeel, send.turbo, send.econo, send.light, send.clean,
send.sleep);
break; break;
} }
#endif // SEND_GREE #endif // SEND_GREE

View File

@ -412,10 +412,6 @@ void IRrecv::pause(void) {
params.rcvstate = kStopState; params.rcvstate = kStopState;
params.rawlen = 0; params.rawlen = 0;
params.overflow = false; params.overflow = false;
#if defined(ESP8266)
os_timer_disarm(&timer);
detachInterrupt(params.recvpin);
#endif
#if defined(ESP32) #if defined(ESP32)
gpio_intr_disable((gpio_num_t)params.recvpin); gpio_intr_disable((gpio_num_t)params.recvpin);
#endif // ESP32 #endif // ESP32
@ -429,10 +425,6 @@ void IRrecv::resume(void) {
params.rcvstate = kIdleState; params.rcvstate = kIdleState;
params.rawlen = 0; params.rawlen = 0;
params.overflow = false; params.overflow = false;
#if defined(ESP8266)
os_timer_setfn(&timer, reinterpret_cast<os_timer_func_t *>(read_timeout),NULL);
attachInterrupt(params.recvpin, gpio_intr, CHANGE);
#endif
#if defined(ESP32) #if defined(ESP32)
timerAlarmDisable(timer); timerAlarmDisable(timer);
gpio_intr_enable((gpio_num_t)params.recvpin); gpio_intr_enable((gpio_num_t)params.recvpin);

View File

@ -58,7 +58,7 @@
// Minor version number (x.X.x) // Minor version number (x.X.x)
#define _IRREMOTEESP8266_VERSION_MINOR 8 #define _IRREMOTEESP8266_VERSION_MINOR 8
// Patch version number (x.x.X) // Patch version number (x.x.X)
#define _IRREMOTEESP8266_VERSION_PATCH 5 #define _IRREMOTEESP8266_VERSION_PATCH 6
// Macro to convert version info into an integer // Macro to convert version info into an integer
#define _IRREMOTEESP8266_VERSION_VAL(major, minor, patch) \ #define _IRREMOTEESP8266_VERSION_VAL(major, minor, patch) \
(((major) << 16) | ((minor) << 8) | (patch)) (((major) << 16) | ((minor) << 8) | (patch))

View File

@ -310,11 +310,12 @@ class IRsend {
void sendSherwood(uint64_t data, uint16_t nbits = kSherwoodBits, void sendSherwood(uint64_t data, uint16_t nbits = kSherwoodBits,
uint16_t repeat = kSherwoodMinRepeat); uint16_t repeat = kSherwoodMinRepeat);
#endif #endif
#if SEND_SAMSUNG // `sendSAMSUNG()` is required by `sendLG()`
#if (SEND_SAMSUNG || SEND_LG)
void sendSAMSUNG(const uint64_t data, const uint16_t nbits = kSamsungBits, void sendSAMSUNG(const uint64_t data, const uint16_t nbits = kSamsungBits,
const uint16_t repeat = kNoRepeat); const uint16_t repeat = kNoRepeat);
uint32_t encodeSAMSUNG(const uint8_t customer, const uint8_t command); uint32_t encodeSAMSUNG(const uint8_t customer, const uint8_t command);
#endif #endif // (SEND_SAMSUNG || SEND_LG)
#if SEND_SAMSUNG36 #if SEND_SAMSUNG36
void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits, void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits,
const uint16_t repeat = kNoRepeat); const uint16_t repeat = kNoRepeat);

View File

@ -145,16 +145,12 @@ String typeToString(const decode_type_t protocol, const bool isRepeat) {
result = kUnknownStr; result = kUnknownStr;
} else { } else {
auto *ptr = reinterpret_cast<const char*>(kAllProtocolNamesStr); auto *ptr = reinterpret_cast<const char*>(kAllProtocolNamesStr);
if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) { for (uint16_t i = 0; i <= protocol && STRLEN(ptr); i++) {
result = kUnknownStr; if (i == protocol) {
} else { result = FPSTR(ptr);
for (uint16_t i = 0; i <= protocol && STRLEN(ptr); i++) { break;
if (i == protocol) {
result = FPSTR(ptr);
break;
}
ptr += STRLEN(ptr) + 1;
} }
ptr += STRLEN(ptr) + 1;
} }
} }
if (isRepeat) { if (isRepeat) {

View File

@ -549,6 +549,9 @@ stdAc::state_t IRCoolixAC::toCommon(const stdAc::state_t *prev) const {
result.mode = toCommonMode(getMode()); result.mode = toCommonMode(getMode());
result.degrees = getTemp(); result.degrees = getTemp();
result.sensorTemperature = getSensorTemp(); result.sensorTemperature = getSensorTemp();
if (result.sensorTemperature == kCoolixSensorTempIgnoreCode) {
result.sensorTemperature = kNoTempValue;
}
result.iFeel = getZoneFollow(); result.iFeel = getZoneFollow();
result.fanspeed = toCommonFanSpeed(getFan()); result.fanspeed = toCommonFanSpeed(getFan());
return result; return result;

View File

@ -13,7 +13,7 @@
// Brand: Kelvinator, Model: KSV70CRC A/C // Brand: Kelvinator, Model: KSV70CRC A/C
// Brand: Kelvinator, Model: KSV70HRC A/C // Brand: Kelvinator, Model: KSV70HRC A/C
// Brand: Kelvinator, Model: KSV80HRC A/C // Brand: Kelvinator, Model: KSV80HRC A/C
// Brand: Green, Model: YAPOF3 remote // Brand: Gree, Model: YAPOF3 remote
// Brand: Gree, Model: YAP0F8 remote // Brand: Gree, Model: YAP0F8 remote
// Brand: Sharp, Model: YB1FA remote // Brand: Sharp, Model: YB1FA remote
// Brand: Sharp, Model: A5VEY A/C // Brand: Sharp, Model: A5VEY A/C

View File

@ -82,7 +82,8 @@ using irutils::addTempToString;
using irutils::addToggleToString; using irutils::addToggleToString;
using irutils::minsToString; using irutils::minsToString;
#if SEND_SAMSUNG // This sending protocol is used by some other protocols. e.g. LG.
#if (SEND_SAMSUNG || SEND_LG)
/// Send a 32-bit Samsung formatted message. /// Send a 32-bit Samsung formatted message.
/// Status: STABLE / Should be working. /// Status: STABLE / Should be working.
/// @param[in] data The message to be sent. /// @param[in] data The message to be sent.
@ -112,7 +113,7 @@ uint32_t IRsend::encodeSAMSUNG(const uint8_t customer, const uint8_t command) {
return ((revcommand ^ 0xFF) | (revcommand << 8) | (revcustomer << 16) | return ((revcommand ^ 0xFF) | (revcommand << 8) | (revcustomer << 16) |
(revcustomer << 24)); (revcustomer << 24));
} }
#endif #endif // (SEND_SAMSUNG || SEND_LG)
#if DECODE_SAMSUNG #if DECODE_SAMSUNG
/// Decode the supplied Samsung 32-bit message. /// Decode the supplied Samsung 32-bit message.

View File

@ -669,7 +669,8 @@ TEST(TestIRac, Fujitsu) {
false, // Turbo (Powerful) false, // Turbo (Powerful)
false, // Econo false, // Econo
true, // Filter true, // Filter
true); // Clean true, // Clean
-1); // Sleep
ASSERT_EQ(ardb1_expected, ac.toString()); ASSERT_EQ(ardb1_expected, ac.toString());
ac._irsend.makeDecodeResult(); ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture)); EXPECT_TRUE(capture.decode(&ac._irsend.capture));
@ -719,7 +720,8 @@ TEST(TestIRac, Fujitsu) {
false, // Turbo (Powerful) false, // Turbo (Powerful)
false, // Econo false, // Econo
true, // Filter true, // Filter
true); // Clean true, // Clean
-1); // Sleep
ASSERT_EQ(arry4_expected, ac.toString()); ASSERT_EQ(arry4_expected, ac.toString());
ac._irsend.makeDecodeResult(); ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture)); EXPECT_TRUE(capture.decode(&ac._irsend.capture));
@ -742,8 +744,9 @@ TEST(TestIRac, Fujitsu) {
false, // Quiet false, // Quiet
false, // Turbo (Powerful) false, // Turbo (Powerful)
false, // Econo false, // Econo
false, // Filter false, // Filter
false); // Clean false, // Clean
-1); // Sleep
ASSERT_EQ(arrew4e_expected, ac.toString()); ASSERT_EQ(arrew4e_expected, ac.toString());
ac._irsend.makeDecodeResult(); ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture)); EXPECT_TRUE(capture.decode(&ac._irsend.capture));

View File

@ -492,6 +492,9 @@ TEST(TestCoolixACClass, SetGetClearSensorTempAndZoneFollow) {
EXPECT_EQ( EXPECT_EQ(
"Power: On, Mode: 3 (Heat), Fan: 6 (Zone Follow), Temp: 24C, " "Power: On, Mode: 3 (Heat), Fan: 6 (Zone Follow), Temp: 24C, "
"Zone Follow: On, Sensor Temp: 19C", ac.toString()); "Zone Follow: On, Sensor Temp: 19C", ac.toString());
// For #Issue2012
EXPECT_EQ(19, ac.getSensorTemp());
EXPECT_EQ(19, ac.toCommon().sensorTemperature);
} }
TEST(TestCoolixACClass, SpecialModesAndReset) { TEST(TestCoolixACClass, SpecialModesAndReset) {
@ -1068,3 +1071,36 @@ TEST(TestDecodeCoolix48, SyntheticSelfDecode) {
"m552s5244", "m552s5244",
irsend.outputStr()); irsend.outputStr());
} }
// Test for issue https://github.com/crankyoldgit/IRremoteESP8266/issues/2012#issuecomment-1650098971
TEST(TestCoolixACClass, Issue2012) {
IRrecv irrecv(kGpioUnused);
IRCoolixAC ac(kGpioUnused);
ac.stateReset();
ac.setRaw(0xB21FD8);
EXPECT_EQ(
"Power: On, Mode: 2 (Auto), Fan: 0 (Auto0), Temp: 26C, "
"Zone Follow: Off, Sensor Temp: Off",
ac.toString());
EXPECT_EQ(kNoTempValue, ac.toCommon().sensorTemperature);
ac.setRaw(0xB21F98);
EXPECT_EQ(
"Power: On, Mode: 2 (Auto), Fan: 0 (Auto0), Temp: 27C, "
"Zone Follow: Off, Sensor Temp: Off",
ac.toString());
EXPECT_EQ(kNoTempValue, ac.toCommon().sensorTemperature);
ac.setRaw(0xB21F88);
EXPECT_EQ(
"Power: On, Mode: 2 (Auto), Fan: 0 (Auto0), Temp: 28C, "
"Zone Follow: Off, Sensor Temp: Off",
ac.toString());
EXPECT_EQ(kNoTempValue, ac.toCommon().sensorTemperature);
ac.setRaw(0xB27BE0);
EXPECT_EQ(
"Power: Off",
ac.toString());
EXPECT_EQ(kNoTempValue, ac.toCommon().sensorTemperature);
}

View File

@ -1,6 +1,6 @@
{ {
"name": "IRremoteESP8266", "name": "IRremoteESP8266",
"version": "2.8.5", "version": "2.8.6",
"keywords": "infrared, ir, remote, esp8266, esp32", "keywords": "infrared, ir, remote, esp8266, esp32",
"description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)", "description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)",
"repository": "repository":

View File

@ -0,0 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.0] - 2021-11-17
Initial release
[0.1.0]: https://github.com/Sensirion/arduino-i2c-sgp41/releases/tag/0.1.0

View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, Sensirion AG
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,78 @@
# Sensirion I2C SGP41 Arduino Library
This is the Sensirion SGP41 library for Arduino using the
modules I2C interface.
[<center><img src="images/SGP41.png" width="300px"></center>](https://www.sensirion.com/en/environmental-sensors/gas-sensors/sgp41)
Click [here](https://www.sensirion.com/en/environmental-sensors/gas-sensors/sgp41) to learn more about the SGP41 sensor.
# Installation
To install, download the latest release as .zip file and add it to your
[Arduino IDE](http://www.arduino.cc/en/main/software) via
Sketch => Include Library => Add .ZIP Library...
Don't forget to **install the dependencies** listed below the same way via `Add
.ZIP Library`
Note: Installation via the Arduino Library Manager is coming soon.
# Dependencies
* [Sensirion Core](https://github.com/Sensirion/arduino-core)
# Quick Start
1. Connect the SGP41 Sensor to your Arduino board's standard
I2C bus. Check the pinout of your Arduino board to find the correct pins.
The pinout of the SGP41 Sensor board can be found in the
data sheet.
* **VDD** of the SEK-SGP41 to the **3.3V** of your Arduino board
* **GND** of the SEK-SGP41 to the **GND** of your Arduino board
* **SCL** of the SEK-SGP41 to the **SCL** of your Arduino board
* **SDA** of the SEK-SGP41 to the **SDA** of your Arduino board
2. Open the `exampleUsage` sample project within the Arduino IDE
File => Examples => Sensirion I2C SGP41 => exampleUsage
3. Click the `Upload` button in the Arduino IDE or
Sketch => Upload
4. When the upload process has finished, open the `Serial Monitor` or `Serial
Plotter` via the `Tools` menu to observe the measurement values. Note that
the `Baud Rate` in the corresponding window has to be set to `115200 baud`.
# Contributing
**Contributions are welcome!**
We develop and test this driver using our company internal tools (version
control, continuous integration, code review etc.) and automatically
synchronize the master branch with GitHub. But this doesn't mean that we don't
respond to issues or don't accept pull requests on GitHub. In fact, you're very
welcome to open issues or create pull requests :)
This Sensirion library uses
[`clang-format`](https://releases.llvm.org/download.html) to standardize the
formatting of all our `.cpp` and `.h` files. Make sure your contributions are
formatted accordingly:
The `-i` flag will apply the format changes to the files listed.
```bash
clang-format -i src/*.cpp src/*.h
```
Note that differences from this formatting will result in a failed build until
they are fixed.
# License
See [LICENSE](LICENSE).

View File

@ -0,0 +1,124 @@
/*
* I2C-Generator: 0.3.0
* Yaml Version: 0.1.0
* Template Version: 0.7.0-62-g3d691f9
*/
/*
* Copyright (c) 2021, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <Arduino.h>
#include <SensirionI2CSgp41.h>
#include <Wire.h>
SensirionI2CSgp41 sgp41;
// time in seconds needed for NOx conditioning
uint16_t conditioning_s = 10;
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(100);
}
Wire.begin();
uint16_t error;
char errorMessage[256];
sgp41.begin(Wire);
uint16_t serialNumber[3];
uint8_t serialNumberSize = 3;
error = sgp41.getSerialNumber(serialNumber, serialNumberSize);
if (error) {
Serial.print("Error trying to execute getSerialNumber(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
} else {
Serial.print("SerialNumber:");
Serial.print("0x");
for (size_t i = 0; i < serialNumberSize; i++) {
uint16_t value = serialNumber[i];
Serial.print(value < 4096 ? "0" : "");
Serial.print(value < 256 ? "0" : "");
Serial.print(value < 16 ? "0" : "");
Serial.print(value, HEX);
}
Serial.println();
}
uint16_t testResult;
error = sgp41.executeSelfTest(testResult);
if (error) {
Serial.print("Error trying to execute executeSelfTest(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
} else if (testResult != 0xD400) {
Serial.print("executeSelfTest failed with error: ");
Serial.println(testResult);
}
}
void loop() {
uint16_t error;
char errorMessage[256];
uint16_t defaultRh = 0x8000;
uint16_t defaultT = 0x6666;
uint16_t srawVoc = 0;
uint16_t srawNox = 0;
delay(1000);
if (conditioning_s > 0) {
// During NOx conditioning (10s) SRAW NOx will remain 0
error = sgp41.executeConditioning(defaultRh, defaultT, srawVoc);
conditioning_s--;
} else {
// Read Measurement
error = sgp41.measureRawSignals(defaultRh, defaultT, srawVoc, srawNox);
}
if (error) {
Serial.print("Error trying to execute measureRawSignals(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
} else {
Serial.print("SRAW_VOC:");
Serial.print(srawVoc);
Serial.print("\t");
Serial.print("SRAW_NOx:");
Serial.println(srawNox);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

View File

@ -0,0 +1,27 @@
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
SensirionI2CSgp41 KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
executeConditioning KEYWORD2
measureRawSignals KEYWORD2
executeSelfTest KEYWORD2
turnHeaterOff KEYWORD2
getSerialNumber KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
sgp41 KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -0,0 +1,10 @@
name=Sensirion I2C SGP41
version=0.1.0
author=Sensirion
maintainer=Sensirion
sentence=Library for the SGP41 sensor family by Sensirion
paragraph=Enables you to use the SGP41 via I2C.
url=https://github.com/Sensirion/arduino-i2c-sgp41
category=Sensors
depends=Sensirion Core
includes=SensirionI2CSgp41.h

View File

@ -0,0 +1,219 @@
/*
* Copyright (c) 2021, Sensirion AG
* Copyright (c) 2023, Andrew Klaus (Removing delay functions)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "SensirionI2CSgp4x.h"
#include "Arduino.h"
#include "SensirionCore.h"
#include <Wire.h>
#define SGP4X_I2C_ADDRESS 0x59
SensirionI2CSgp4x::SensirionI2CSgp4x() {
}
void SensirionI2CSgp4x::begin(TwoWire& i2cBus) {
_i2cBus = &i2cBus;
}
uint16_t SensirionI2CSgp4x::sendConditioningCmd(uint16_t defaultRh,
uint16_t defaultT) {
uint16_t error;
uint8_t buffer[8];
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2612, buffer, 8);
error = txFrame.addUInt16(defaultRh);
error |= txFrame.addUInt16(defaultT);
if (error) {
return error;
}
error = SensirionI2CCommunication::sendFrame(SGP4X_I2C_ADDRESS, txFrame,
*_i2cBus);
return error;
}
uint16_t SensirionI2CSgp4x::readConditioningValue(uint16_t& srawVoc){
// This must run at least 50ms after initiateConditioning
uint16_t error;
uint8_t buffer[8];
SensirionI2CRxFrame rxFrame(buffer, 8);
error = SensirionI2CCommunication::receiveFrame(SGP4X_I2C_ADDRESS, 3,
rxFrame, *_i2cBus);
if (error) {
return error;
}
error |= rxFrame.getUInt16(srawVoc);
return error;
}
uint16_t SensirionI2CSgp4x::sendRawSignalsCmd(uint16_t relativeHumidity,
uint16_t temperature) {
uint16_t error;
uint8_t buffer[8];
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x2619, buffer, 8);
error = txFrame.addUInt16(relativeHumidity);
error |= txFrame.addUInt16(temperature);
if (error) {
return error;
}
error = SensirionI2CCommunication::sendFrame(SGP4X_I2C_ADDRESS, txFrame,
*_i2cBus);
return error;
}
uint16_t SensirionI2CSgp4x::readRawSignalsValue(uint16_t& srawVoc,
uint16_t& srawNox) {
// This must run 50ms after initiateRawSignals
uint16_t error;
uint8_t buffer[6];
SensirionI2CRxFrame rxFrame(buffer, 6);
error = SensirionI2CCommunication::receiveFrame(SGP4X_I2C_ADDRESS, 6,
rxFrame, *_i2cBus);
if (error) {
return error;
}
error |= rxFrame.getUInt16(srawVoc);
error |= rxFrame.getUInt16(srawNox);
return error;
}
uint16_t SensirionI2CSgp4x::sendRawSignalCmd(uint16_t relativeHumidity,
uint16_t temperature) {
uint16_t error;
uint8_t buffer[8];
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x260F, buffer, 8);
error = txFrame.addUInt16(relativeHumidity);
error |= txFrame.addUInt16(temperature);
if (error) {
return error;
}
error = SensirionI2CCommunication::sendFrame(SGP4X_I2C_ADDRESS, txFrame,
*_i2cBus);
return error;
}
uint16_t SensirionI2CSgp4x::readRawSignalValue(uint16_t& srawVoc) {
uint16_t error;
uint8_t buffer[8];
SensirionI2CRxFrame rxFrame(buffer, 8);
error = SensirionI2CCommunication::receiveFrame(SGP4X_I2C_ADDRESS, 3,
rxFrame, *_i2cBus);
if (error) {
return error;
}
error |= rxFrame.getUInt16(srawVoc);
return error;
}
uint16_t SensirionI2CSgp4x::sendSelfTestCmd() {
uint16_t error;
uint8_t buffer[3];
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x280E, buffer, 3);
error = SensirionI2CCommunication::sendFrame(SGP4X_I2C_ADDRESS, txFrame,
*_i2cBus);
return error;
}
uint16_t SensirionI2CSgp4x::readSelfTestValue(uint16_t& testResult) {
// Must run 320ms after initiateSelfTest
uint16_t error;
uint8_t buffer[3];
SensirionI2CRxFrame rxFrame(buffer, 3);
error = SensirionI2CCommunication::receiveFrame(SGP4X_I2C_ADDRESS, 3,
rxFrame, *_i2cBus);
if (error) {
return error;
}
error |= rxFrame.getUInt16(testResult);
return error;
}
uint16_t SensirionI2CSgp4x::turnHeaterOff() {
uint16_t error;
uint8_t buffer[2];
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x3615, buffer, 2);
error = SensirionI2CCommunication::sendFrame(SGP4X_I2C_ADDRESS, txFrame,
*_i2cBus);
delay(1);
return error;
}
uint16_t SensirionI2CSgp4x::getSerialNumber(uint16_t serialNumber[],
uint8_t serialNumberSize) {
uint16_t error;
uint8_t buffer[9];
SensirionI2CTxFrame txFrame =
SensirionI2CTxFrame::createWithUInt16Command(0x3682, buffer, 9);
error = SensirionI2CCommunication::sendFrame(SGP4X_I2C_ADDRESS, txFrame,
*_i2cBus);
if (error) {
return error;
}
delay(1);
SensirionI2CRxFrame rxFrame(buffer, 9);
error = SensirionI2CCommunication::receiveFrame(SGP4X_I2C_ADDRESS, 9,
rxFrame, *_i2cBus);
if (error) {
return error;
}
error |= rxFrame.getUInt16(serialNumber[0]);
error |= rxFrame.getUInt16(serialNumber[1]);
error |= rxFrame.getUInt16(serialNumber[2]);
return error;
}

View File

@ -0,0 +1,145 @@
/*
* THIS FILE IS AUTOMATICALLY GENERATED
*
* I2C-Generator: 0.3.0
* Yaml Version: 0.1.0
* Template Version: 0.7.0-62-g3d691f9
*/
/*
* Copyright (c) 2021, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRIONI2CSGP4X_H
#define SENSIRIONI2CSGP4X_H
#include <Wire.h>
#include <SensirionCore.h>
class SensirionI2CSgp4x {
public:
SensirionI2CSgp4x();
/**
* begin() - Initializes the SensirionI2CSgp4x class.
*
* @param serial Arduino stream object to be communicated with.
*
*/
void begin(TwoWire& i2cBus);
/**
* executeConditioning() - This command starts the conditioning, i.e., the
* VOC pixel will be operated at the same temperature as it is by calling
* the sgp41_measure_raw command while the NOx pixel will be operated at a
* different temperature for conditioning. This command returns only the
* measured raw signal of the VOC pixel SRAW_VOC as 2 bytes (+ 1 CRC byte).
*
* @param defaultRh Default conditions for relative humidty.
*
* @param defaultT Default conditions for temperature.
*
* @param srawVoc u16 unsigned integer directly provides the raw signal
* SRAW_VOC in ticks which is proportional to the logarithm of the
* resistance of the sensing element.
*
* @return 0 on success, an error code otherwise
*/
uint16_t sendConditioningCmd(uint16_t defaultRh, uint16_t defaultT);
uint16_t readConditioningValue(uint16_t& srawVoc);
/**
* measureRawSignals() - This command starts/continues the VOC+NOx
* measurement mode
*
* @param relativeHumidity Leaves humidity compensation disabled by sending
* the default value 0x8000 (50%RH) or enables humidity compensation when
* sending the relative humidity in ticks (ticks = %RH * 65535 / 100)
*
* @param temperature Leaves humidity compensation disabled by sending the
* default value 0x6666 (25 degC) or enables humidity compensation when
* sending the temperature in ticks (ticks = (degC + 45) * 65535 / 175)
*
* @param srawVoc u16 unsigned integer directly provides the raw signal
* SRAW_VOC in ticks which is proportional to the logarithm of the
* resistance of the sensing element.
*
* @param srawNox u16 unsigned integer directly provides the raw signal
* SRAW_NOX in ticks which is proportional to the logarithm of the
* resistance of the sensing element.
*
* @return 0 on success, an error code otherwise
*/
uint16_t sendRawSignalsCmd(uint16_t relativeHumidity, uint16_t temperature);
uint16_t readRawSignalsValue(uint16_t& srawVoc, uint16_t& srawNox);
uint16_t sendRawSignalCmd(uint16_t relativeHumidity, uint16_t temperature);
uint16_t readRawSignalValue(uint16_t& srawVoc);
/**
* executeSelfTest() - This command triggers the built-in self-test checking
* for integrity of both hotplate and MOX material and returns the result of
* this test as 2 bytes
*
* @param testResult 0xXX 0xYY: ignore most significant byte 0xXX. The four
* least significant bits of the least significant byte 0xYY provide
* information if the self-test has or has not passed for each individual
* pixel. All zero mean all tests passed successfully. Check the datasheet
* for more detailed information.
*
* @return 0 on success, an error code otherwise
*/
uint16_t sendSelfTestCmd(void);
uint16_t readSelfTestValue(uint16_t& testResult);
/**
* turnHeaterOff() - This command turns the hotplate off and stops the
* measurement. Subsequently, the sensor enters the idle mode.
*
* @return 0 on success, an error code otherwise
*/
uint16_t turnHeaterOff(void);
/**
* getSerialNumber() - This command provides the decimal serial number of
* the SGP41 chip by returning 3x2 bytes.
*
* @param serialNumber 48-bit unique serial number
*
* @return 0 on success, an error code otherwise
*/
uint16_t getSerialNumber(uint16_t serialNumber[], uint8_t serialNumberSize);
private:
TwoWire* _i2cBus = nullptr;
};
#endif /* SENSIRIONI2CSGP4X_H */

View File

@ -0,0 +1,582 @@
/*
* Copyright (c) 2022, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "sensirion_gas_index_algorithm.h"
#include <math.h>
static void GasIndexAlgorithm__init_instances(GasIndexAlgorithmParams* params);
static void GasIndexAlgorithm__mean_variance_estimator__set_parameters(
GasIndexAlgorithmParams* params);
static void GasIndexAlgorithm__mean_variance_estimator__set_states(
GasIndexAlgorithmParams* params, float mean, float std, float uptime_gamma);
static float GasIndexAlgorithm__mean_variance_estimator__get_std(
const GasIndexAlgorithmParams* params);
static float GasIndexAlgorithm__mean_variance_estimator__get_mean(
const GasIndexAlgorithmParams* params);
static bool GasIndexAlgorithm__mean_variance_estimator__is_initialized(
GasIndexAlgorithmParams* params);
static void GasIndexAlgorithm__mean_variance_estimator___calculate_gamma(
GasIndexAlgorithmParams* params);
static void GasIndexAlgorithm__mean_variance_estimator__process(
GasIndexAlgorithmParams* params, float sraw);
static void
GasIndexAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
GasIndexAlgorithmParams* params, float X0, float K);
static float GasIndexAlgorithm__mean_variance_estimator___sigmoid__process(
GasIndexAlgorithmParams* params, float sample);
static void
GasIndexAlgorithm__mox_model__set_parameters(GasIndexAlgorithmParams* params,
float SRAW_STD, float SRAW_MEAN);
static float
GasIndexAlgorithm__mox_model__process(GasIndexAlgorithmParams* params,
float sraw);
static void GasIndexAlgorithm__sigmoid_scaled__set_parameters(
GasIndexAlgorithmParams* params, float X0, float K, float offset_default);
static float
GasIndexAlgorithm__sigmoid_scaled__process(GasIndexAlgorithmParams* params,
float sample);
static void GasIndexAlgorithm__adaptive_lowpass__set_parameters(
GasIndexAlgorithmParams* params);
static float
GasIndexAlgorithm__adaptive_lowpass__process(GasIndexAlgorithmParams* params,
float sample);
void GasIndexAlgorithm_init_with_sampling_interval(
GasIndexAlgorithmParams* params, int32_t algorithm_type,
float sampling_interval) {
params->mAlgorithm_Type = algorithm_type;
params->mSamplingInterval = sampling_interval;
if ((algorithm_type == GasIndexAlgorithm_ALGORITHM_TYPE_NOX)) {
params->mIndex_Offset = GasIndexAlgorithm_NOX_INDEX_OFFSET_DEFAULT;
params->mSraw_Minimum = GasIndexAlgorithm_NOX_SRAW_MINIMUM;
params->mGating_Max_Duration_Minutes =
GasIndexAlgorithm_GATING_NOX_MAX_DURATION_MINUTES;
params->mInit_Duration_Mean = GasIndexAlgorithm_INIT_DURATION_MEAN_NOX;
params->mInit_Duration_Variance =
GasIndexAlgorithm_INIT_DURATION_VARIANCE_NOX;
params->mGating_Threshold = GasIndexAlgorithm_GATING_THRESHOLD_NOX;
} else {
params->mIndex_Offset = GasIndexAlgorithm_VOC_INDEX_OFFSET_DEFAULT;
params->mSraw_Minimum = GasIndexAlgorithm_VOC_SRAW_MINIMUM;
params->mGating_Max_Duration_Minutes =
GasIndexAlgorithm_GATING_VOC_MAX_DURATION_MINUTES;
params->mInit_Duration_Mean = GasIndexAlgorithm_INIT_DURATION_MEAN_VOC;
params->mInit_Duration_Variance =
GasIndexAlgorithm_INIT_DURATION_VARIANCE_VOC;
params->mGating_Threshold = GasIndexAlgorithm_GATING_THRESHOLD_VOC;
}
params->mIndex_Gain = GasIndexAlgorithm_INDEX_GAIN;
params->mTau_Mean_Hours = GasIndexAlgorithm_TAU_MEAN_HOURS;
params->mTau_Variance_Hours = GasIndexAlgorithm_TAU_VARIANCE_HOURS;
params->mSraw_Std_Initial = GasIndexAlgorithm_SRAW_STD_INITIAL;
GasIndexAlgorithm_reset(params);
}
void GasIndexAlgorithm_init(GasIndexAlgorithmParams* params,
int32_t algorithm_type) {
GasIndexAlgorithm_init_with_sampling_interval(
params, algorithm_type, GasIndexAlgorithm_DEFAULT_SAMPLING_INTERVAL);
}
void GasIndexAlgorithm_reset(GasIndexAlgorithmParams* params) {
params->mUptime = 0.f;
params->mSraw = 0.f;
params->mGas_Index = 0;
GasIndexAlgorithm__init_instances(params);
}
static void GasIndexAlgorithm__init_instances(GasIndexAlgorithmParams* params) {
GasIndexAlgorithm__mean_variance_estimator__set_parameters(params);
GasIndexAlgorithm__mox_model__set_parameters(
params, GasIndexAlgorithm__mean_variance_estimator__get_std(params),
GasIndexAlgorithm__mean_variance_estimator__get_mean(params));
if ((params->mAlgorithm_Type == GasIndexAlgorithm_ALGORITHM_TYPE_NOX)) {
GasIndexAlgorithm__sigmoid_scaled__set_parameters(
params, GasIndexAlgorithm_SIGMOID_X0_NOX,
GasIndexAlgorithm_SIGMOID_K_NOX,
GasIndexAlgorithm_NOX_INDEX_OFFSET_DEFAULT);
} else {
GasIndexAlgorithm__sigmoid_scaled__set_parameters(
params, GasIndexAlgorithm_SIGMOID_X0_VOC,
GasIndexAlgorithm_SIGMOID_K_VOC,
GasIndexAlgorithm_VOC_INDEX_OFFSET_DEFAULT);
}
GasIndexAlgorithm__adaptive_lowpass__set_parameters(params);
}
void GasIndexAlgorithm_get_sampling_interval(
const GasIndexAlgorithmParams* params, float* sampling_interval) {
*sampling_interval = params->mSamplingInterval;
}
void GasIndexAlgorithm_get_states(const GasIndexAlgorithmParams* params,
float* state0, float* state1) {
*state0 = GasIndexAlgorithm__mean_variance_estimator__get_mean(params);
*state1 = GasIndexAlgorithm__mean_variance_estimator__get_std(params);
return;
}
void GasIndexAlgorithm_set_states(GasIndexAlgorithmParams* params, float state0,
float state1) {
GasIndexAlgorithm__mean_variance_estimator__set_states(
params, state0, state1, GasIndexAlgorithm_PERSISTENCE_UPTIME_GAMMA);
GasIndexAlgorithm__mox_model__set_parameters(
params, GasIndexAlgorithm__mean_variance_estimator__get_std(params),
GasIndexAlgorithm__mean_variance_estimator__get_mean(params));
params->mSraw = state0;
}
void GasIndexAlgorithm_set_tuning_parameters(
GasIndexAlgorithmParams* params, int32_t index_offset,
int32_t learning_time_offset_hours, int32_t learning_time_gain_hours,
int32_t gating_max_duration_minutes, int32_t std_initial,
int32_t gain_factor) {
params->mIndex_Offset = ((float)(index_offset));
params->mTau_Mean_Hours = ((float)(learning_time_offset_hours));
params->mTau_Variance_Hours = ((float)(learning_time_gain_hours));
params->mGating_Max_Duration_Minutes =
((float)(gating_max_duration_minutes));
params->mSraw_Std_Initial = ((float)(std_initial));
params->mIndex_Gain = ((float)(gain_factor));
GasIndexAlgorithm__init_instances(params);
}
void GasIndexAlgorithm_get_tuning_parameters(
const GasIndexAlgorithmParams* params, int32_t* index_offset,
int32_t* learning_time_offset_hours, int32_t* learning_time_gain_hours,
int32_t* gating_max_duration_minutes, int32_t* std_initial,
int32_t* gain_factor) {
*index_offset = ((int32_t)(params->mIndex_Offset));
*learning_time_offset_hours = ((int32_t)(params->mTau_Mean_Hours));
*learning_time_gain_hours = ((int32_t)(params->mTau_Variance_Hours));
*gating_max_duration_minutes =
((int32_t)(params->mGating_Max_Duration_Minutes));
*std_initial = ((int32_t)(params->mSraw_Std_Initial));
*gain_factor = ((int32_t)(params->mIndex_Gain));
return;
}
void GasIndexAlgorithm_process(GasIndexAlgorithmParams* params, int32_t sraw,
int32_t* gas_index) {
if ((params->mUptime <= GasIndexAlgorithm_INITIAL_BLACKOUT)) {
params->mUptime = (params->mUptime + params->mSamplingInterval);
} else {
if (((sraw > 0) && (sraw < 65000))) {
if ((sraw < (params->mSraw_Minimum + 1))) {
sraw = (params->mSraw_Minimum + 1);
} else if ((sraw > (params->mSraw_Minimum + 32767))) {
sraw = (params->mSraw_Minimum + 32767);
}
params->mSraw = ((float)((sraw - params->mSraw_Minimum)));
}
if (((params->mAlgorithm_Type ==
GasIndexAlgorithm_ALGORITHM_TYPE_VOC) ||
GasIndexAlgorithm__mean_variance_estimator__is_initialized(
params))) {
params->mGas_Index =
GasIndexAlgorithm__mox_model__process(params, params->mSraw);
params->mGas_Index = GasIndexAlgorithm__sigmoid_scaled__process(
params, params->mGas_Index);
} else {
params->mGas_Index = params->mIndex_Offset;
}
params->mGas_Index = GasIndexAlgorithm__adaptive_lowpass__process(
params, params->mGas_Index);
if ((params->mGas_Index < 0.5f)) {
params->mGas_Index = 0.5f;
}
if ((params->mSraw > 0.f)) {
GasIndexAlgorithm__mean_variance_estimator__process(params,
params->mSraw);
GasIndexAlgorithm__mox_model__set_parameters(
params,
GasIndexAlgorithm__mean_variance_estimator__get_std(params),
GasIndexAlgorithm__mean_variance_estimator__get_mean(params));
}
}
*gas_index = ((int32_t)((params->mGas_Index + 0.5f)));
return;
}
static void GasIndexAlgorithm__mean_variance_estimator__set_parameters(
GasIndexAlgorithmParams* params) {
params->m_Mean_Variance_Estimator___Initialized = false;
params->m_Mean_Variance_Estimator___Mean = 0.f;
params->m_Mean_Variance_Estimator___Sraw_Offset = 0.f;
params->m_Mean_Variance_Estimator___Std = params->mSraw_Std_Initial;
params->m_Mean_Variance_Estimator___Gamma_Mean =
(((GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__ADDITIONAL_GAMMA_MEAN_SCALING *
GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) *
(params->mSamplingInterval / 3600.f)) /
(params->mTau_Mean_Hours + (params->mSamplingInterval / 3600.f)));
params->m_Mean_Variance_Estimator___Gamma_Variance =
((GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
(params->mSamplingInterval / 3600.f)) /
(params->mTau_Variance_Hours + (params->mSamplingInterval / 3600.f)));
if ((params->mAlgorithm_Type == GasIndexAlgorithm_ALGORITHM_TYPE_NOX)) {
params->m_Mean_Variance_Estimator___Gamma_Initial_Mean =
(((GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__ADDITIONAL_GAMMA_MEAN_SCALING *
GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) *
params->mSamplingInterval) /
(GasIndexAlgorithm_TAU_INITIAL_MEAN_NOX +
params->mSamplingInterval));
} else {
params->m_Mean_Variance_Estimator___Gamma_Initial_Mean =
(((GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__ADDITIONAL_GAMMA_MEAN_SCALING *
GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) *
params->mSamplingInterval) /
(GasIndexAlgorithm_TAU_INITIAL_MEAN_VOC +
params->mSamplingInterval));
}
params->m_Mean_Variance_Estimator___Gamma_Initial_Variance =
((GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
params->mSamplingInterval) /
(GasIndexAlgorithm_TAU_INITIAL_VARIANCE + params->mSamplingInterval));
params->m_Mean_Variance_Estimator__Gamma_Mean = 0.f;
params->m_Mean_Variance_Estimator__Gamma_Variance = 0.f;
params->m_Mean_Variance_Estimator___Uptime_Gamma = 0.f;
params->m_Mean_Variance_Estimator___Uptime_Gating = 0.f;
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = 0.f;
}
static void GasIndexAlgorithm__mean_variance_estimator__set_states(
GasIndexAlgorithmParams* params, float mean, float std,
float uptime_gamma) {
params->m_Mean_Variance_Estimator___Mean = mean;
params->m_Mean_Variance_Estimator___Std = std;
params->m_Mean_Variance_Estimator___Uptime_Gamma = uptime_gamma;
params->m_Mean_Variance_Estimator___Initialized = true;
}
static float GasIndexAlgorithm__mean_variance_estimator__get_std(
const GasIndexAlgorithmParams* params) {
return params->m_Mean_Variance_Estimator___Std;
}
static float GasIndexAlgorithm__mean_variance_estimator__get_mean(
const GasIndexAlgorithmParams* params) {
return (params->m_Mean_Variance_Estimator___Mean +
params->m_Mean_Variance_Estimator___Sraw_Offset);
}
static bool GasIndexAlgorithm__mean_variance_estimator__is_initialized(
GasIndexAlgorithmParams* params) {
return params->m_Mean_Variance_Estimator___Initialized;
}
static void GasIndexAlgorithm__mean_variance_estimator___calculate_gamma(
GasIndexAlgorithmParams* params) {
float uptime_limit;
float sigmoid_gamma_mean;
float gamma_mean;
float gating_threshold_mean;
float sigmoid_gating_mean;
float sigmoid_gamma_variance;
float gamma_variance;
float gating_threshold_variance;
float sigmoid_gating_variance;
uptime_limit = (GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX -
params->mSamplingInterval);
if ((params->m_Mean_Variance_Estimator___Uptime_Gamma < uptime_limit)) {
params->m_Mean_Variance_Estimator___Uptime_Gamma =
(params->m_Mean_Variance_Estimator___Uptime_Gamma +
params->mSamplingInterval);
}
if ((params->m_Mean_Variance_Estimator___Uptime_Gating < uptime_limit)) {
params->m_Mean_Variance_Estimator___Uptime_Gating =
(params->m_Mean_Variance_Estimator___Uptime_Gating +
params->mSamplingInterval);
}
GasIndexAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
params, params->mInit_Duration_Mean,
GasIndexAlgorithm_INIT_TRANSITION_MEAN);
sigmoid_gamma_mean =
GasIndexAlgorithm__mean_variance_estimator___sigmoid__process(
params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
gamma_mean = (params->m_Mean_Variance_Estimator___Gamma_Mean +
((params->m_Mean_Variance_Estimator___Gamma_Initial_Mean -
params->m_Mean_Variance_Estimator___Gamma_Mean) *
sigmoid_gamma_mean));
gating_threshold_mean =
(params->mGating_Threshold +
((GasIndexAlgorithm_GATING_THRESHOLD_INITIAL -
params->mGating_Threshold) *
GasIndexAlgorithm__mean_variance_estimator___sigmoid__process(
params, params->m_Mean_Variance_Estimator___Uptime_Gating)));
GasIndexAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
params, gating_threshold_mean,
GasIndexAlgorithm_GATING_THRESHOLD_TRANSITION);
sigmoid_gating_mean =
GasIndexAlgorithm__mean_variance_estimator___sigmoid__process(
params, params->mGas_Index);
params->m_Mean_Variance_Estimator__Gamma_Mean =
(sigmoid_gating_mean * gamma_mean);
GasIndexAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
params, params->mInit_Duration_Variance,
GasIndexAlgorithm_INIT_TRANSITION_VARIANCE);
sigmoid_gamma_variance =
GasIndexAlgorithm__mean_variance_estimator___sigmoid__process(
params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
gamma_variance =
(params->m_Mean_Variance_Estimator___Gamma_Variance +
((params->m_Mean_Variance_Estimator___Gamma_Initial_Variance -
params->m_Mean_Variance_Estimator___Gamma_Variance) *
(sigmoid_gamma_variance - sigmoid_gamma_mean)));
gating_threshold_variance =
(params->mGating_Threshold +
((GasIndexAlgorithm_GATING_THRESHOLD_INITIAL -
params->mGating_Threshold) *
GasIndexAlgorithm__mean_variance_estimator___sigmoid__process(
params, params->m_Mean_Variance_Estimator___Uptime_Gating)));
GasIndexAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
params, gating_threshold_variance,
GasIndexAlgorithm_GATING_THRESHOLD_TRANSITION);
sigmoid_gating_variance =
GasIndexAlgorithm__mean_variance_estimator___sigmoid__process(
params, params->mGas_Index);
params->m_Mean_Variance_Estimator__Gamma_Variance =
(sigmoid_gating_variance * gamma_variance);
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes =
(params->m_Mean_Variance_Estimator___Gating_Duration_Minutes +
((params->mSamplingInterval / 60.f) *
(((1.f - sigmoid_gating_mean) *
(1.f + GasIndexAlgorithm_GATING_MAX_RATIO)) -
GasIndexAlgorithm_GATING_MAX_RATIO)));
if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes < 0.f)) {
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = 0.f;
}
if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes >
params->mGating_Max_Duration_Minutes)) {
params->m_Mean_Variance_Estimator___Uptime_Gating = 0.f;
}
}
static void GasIndexAlgorithm__mean_variance_estimator__process(
GasIndexAlgorithmParams* params, float sraw) {
float delta_sgp;
float c;
float additional_scaling;
if ((params->m_Mean_Variance_Estimator___Initialized == false)) {
params->m_Mean_Variance_Estimator___Initialized = true;
params->m_Mean_Variance_Estimator___Sraw_Offset = sraw;
params->m_Mean_Variance_Estimator___Mean = 0.f;
} else {
if (((params->m_Mean_Variance_Estimator___Mean >= 100.f) ||
(params->m_Mean_Variance_Estimator___Mean <= -100.f))) {
params->m_Mean_Variance_Estimator___Sraw_Offset =
(params->m_Mean_Variance_Estimator___Sraw_Offset +
params->m_Mean_Variance_Estimator___Mean);
params->m_Mean_Variance_Estimator___Mean = 0.f;
}
sraw = (sraw - params->m_Mean_Variance_Estimator___Sraw_Offset);
GasIndexAlgorithm__mean_variance_estimator___calculate_gamma(params);
delta_sgp = ((sraw - params->m_Mean_Variance_Estimator___Mean) /
GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING);
if ((delta_sgp < 0.f)) {
c = (params->m_Mean_Variance_Estimator___Std - delta_sgp);
} else {
c = (params->m_Mean_Variance_Estimator___Std + delta_sgp);
}
additional_scaling = 1.f;
if ((c > 1440.f)) {
additional_scaling = ((c / 1440.f) * (c / 1440.f));
}
params->m_Mean_Variance_Estimator___Std =
(sqrtf((additional_scaling *
(GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING -
params->m_Mean_Variance_Estimator__Gamma_Variance))) *
sqrtf(
((params->m_Mean_Variance_Estimator___Std *
(params->m_Mean_Variance_Estimator___Std /
(GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
additional_scaling))) +
(((params->m_Mean_Variance_Estimator__Gamma_Variance *
delta_sgp) /
additional_scaling) *
delta_sgp))));
params->m_Mean_Variance_Estimator___Mean =
(params->m_Mean_Variance_Estimator___Mean +
((params->m_Mean_Variance_Estimator__Gamma_Mean * delta_sgp) /
GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__ADDITIONAL_GAMMA_MEAN_SCALING));
}
}
static void
GasIndexAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
GasIndexAlgorithmParams* params, float X0, float K) {
params->m_Mean_Variance_Estimator___Sigmoid__K = K;
params->m_Mean_Variance_Estimator___Sigmoid__X0 = X0;
}
static float GasIndexAlgorithm__mean_variance_estimator___sigmoid__process(
GasIndexAlgorithmParams* params, float sample) {
float x;
x = (params->m_Mean_Variance_Estimator___Sigmoid__K *
(sample - params->m_Mean_Variance_Estimator___Sigmoid__X0));
if ((x < -50.f)) {
return 1.f;
} else if ((x > 50.f)) {
return 0.f;
} else {
return (1.f / (1.f + expf(x)));
}
}
static void
GasIndexAlgorithm__mox_model__set_parameters(GasIndexAlgorithmParams* params,
float SRAW_STD, float SRAW_MEAN) {
params->m_Mox_Model__Sraw_Std = SRAW_STD;
params->m_Mox_Model__Sraw_Mean = SRAW_MEAN;
}
static float
GasIndexAlgorithm__mox_model__process(GasIndexAlgorithmParams* params,
float sraw) {
if ((params->mAlgorithm_Type == GasIndexAlgorithm_ALGORITHM_TYPE_NOX)) {
return (((sraw - params->m_Mox_Model__Sraw_Mean) /
GasIndexAlgorithm_SRAW_STD_NOX) *
params->mIndex_Gain);
} else {
return (((sraw - params->m_Mox_Model__Sraw_Mean) /
(-1.f * (params->m_Mox_Model__Sraw_Std +
GasIndexAlgorithm_SRAW_STD_BONUS_VOC))) *
params->mIndex_Gain);
}
}
static void GasIndexAlgorithm__sigmoid_scaled__set_parameters(
GasIndexAlgorithmParams* params, float X0, float K, float offset_default) {
params->m_Sigmoid_Scaled__K = K;
params->m_Sigmoid_Scaled__X0 = X0;
params->m_Sigmoid_Scaled__Offset_Default = offset_default;
}
static float
GasIndexAlgorithm__sigmoid_scaled__process(GasIndexAlgorithmParams* params,
float sample) {
float x;
float shift;
x = (params->m_Sigmoid_Scaled__K * (sample - params->m_Sigmoid_Scaled__X0));
if ((x < -50.f)) {
return GasIndexAlgorithm_SIGMOID_L;
} else if ((x > 50.f)) {
return 0.f;
} else {
if ((sample >= 0.f)) {
if ((params->m_Sigmoid_Scaled__Offset_Default == 1.f)) {
shift = ((500.f / 499.f) * (1.f - params->mIndex_Offset));
} else {
shift = ((GasIndexAlgorithm_SIGMOID_L -
(5.f * params->mIndex_Offset)) /
4.f);
}
return (((GasIndexAlgorithm_SIGMOID_L + shift) / (1.f + expf(x))) -
shift);
} else {
return ((params->mIndex_Offset /
params->m_Sigmoid_Scaled__Offset_Default) *
(GasIndexAlgorithm_SIGMOID_L / (1.f + expf(x))));
}
}
}
static void GasIndexAlgorithm__adaptive_lowpass__set_parameters(
GasIndexAlgorithmParams* params) {
params->m_Adaptive_Lowpass__A1 =
(params->mSamplingInterval /
(GasIndexAlgorithm_LP_TAU_FAST + params->mSamplingInterval));
params->m_Adaptive_Lowpass__A2 =
(params->mSamplingInterval /
(GasIndexAlgorithm_LP_TAU_SLOW + params->mSamplingInterval));
params->m_Adaptive_Lowpass___Initialized = false;
}
static float
GasIndexAlgorithm__adaptive_lowpass__process(GasIndexAlgorithmParams* params,
float sample) {
float abs_delta;
float F1;
float tau_a;
float a3;
if ((params->m_Adaptive_Lowpass___Initialized == false)) {
params->m_Adaptive_Lowpass___X1 = sample;
params->m_Adaptive_Lowpass___X2 = sample;
params->m_Adaptive_Lowpass___X3 = sample;
params->m_Adaptive_Lowpass___Initialized = true;
}
params->m_Adaptive_Lowpass___X1 =
(((1.f - params->m_Adaptive_Lowpass__A1) *
params->m_Adaptive_Lowpass___X1) +
(params->m_Adaptive_Lowpass__A1 * sample));
params->m_Adaptive_Lowpass___X2 =
(((1.f - params->m_Adaptive_Lowpass__A2) *
params->m_Adaptive_Lowpass___X2) +
(params->m_Adaptive_Lowpass__A2 * sample));
abs_delta =
(params->m_Adaptive_Lowpass___X1 - params->m_Adaptive_Lowpass___X2);
if ((abs_delta < 0.f)) {
abs_delta = (-1.f * abs_delta);
}
F1 = expf((GasIndexAlgorithm_LP_ALPHA * abs_delta));
tau_a = (((GasIndexAlgorithm_LP_TAU_SLOW - GasIndexAlgorithm_LP_TAU_FAST) *
F1) +
GasIndexAlgorithm_LP_TAU_FAST);
a3 = (params->mSamplingInterval / (params->mSamplingInterval + tau_a));
params->m_Adaptive_Lowpass___X3 =
(((1.f - a3) * params->m_Adaptive_Lowpass___X3) + (a3 * sample));
return params->m_Adaptive_Lowpass___X3;
}

View File

@ -0,0 +1,290 @@
/*
* Copyright (c) 2022, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef GASINDEXALGORITHM_H_
#define GASINDEXALGORITHM_H_
#include <stdint.h>
#ifndef __cplusplus
#if __STDC_VERSION__ >= 199901L
#include <stdbool.h>
#else
#ifndef bool
#define bool int
#define true 1
#define false 0
#endif // bool
#endif // __STDC_VERSION__
#endif // __cplusplus
// Should be set by the building toolchain
#ifndef LIBRARY_VERSION_NAME
#define LIBRARY_VERSION_NAME "3.2.0"
#endif
#define GasIndexAlgorithm_ALGORITHM_TYPE_VOC (0)
#define GasIndexAlgorithm_ALGORITHM_TYPE_NOX (1)
#define GasIndexAlgorithm_DEFAULT_SAMPLING_INTERVAL (1.f)
#define GasIndexAlgorithm_INITIAL_BLACKOUT (45.f)
#define GasIndexAlgorithm_INDEX_GAIN (230.f)
#define GasIndexAlgorithm_SRAW_STD_INITIAL (50.f)
#define GasIndexAlgorithm_SRAW_STD_BONUS_VOC (220.f)
#define GasIndexAlgorithm_SRAW_STD_NOX (2000.f)
#define GasIndexAlgorithm_TAU_MEAN_HOURS (12.f)
#define GasIndexAlgorithm_TAU_VARIANCE_HOURS (12.f)
#define GasIndexAlgorithm_TAU_INITIAL_MEAN_VOC (20.f)
#define GasIndexAlgorithm_TAU_INITIAL_MEAN_NOX (1200.f)
#define GasIndexAlgorithm_INIT_DURATION_MEAN_VOC ((3600.f * 0.75f))
#define GasIndexAlgorithm_INIT_DURATION_MEAN_NOX ((3600.f * 4.75f))
#define GasIndexAlgorithm_INIT_TRANSITION_MEAN (0.01f)
#define GasIndexAlgorithm_TAU_INITIAL_VARIANCE (2500.f)
#define GasIndexAlgorithm_INIT_DURATION_VARIANCE_VOC ((3600.f * 1.45f))
#define GasIndexAlgorithm_INIT_DURATION_VARIANCE_NOX ((3600.f * 5.70f))
#define GasIndexAlgorithm_INIT_TRANSITION_VARIANCE (0.01f)
#define GasIndexAlgorithm_GATING_THRESHOLD_VOC (340.f)
#define GasIndexAlgorithm_GATING_THRESHOLD_NOX (30.f)
#define GasIndexAlgorithm_GATING_THRESHOLD_INITIAL (510.f)
#define GasIndexAlgorithm_GATING_THRESHOLD_TRANSITION (0.09f)
#define GasIndexAlgorithm_GATING_VOC_MAX_DURATION_MINUTES ((60.f * 3.f))
#define GasIndexAlgorithm_GATING_NOX_MAX_DURATION_MINUTES ((60.f * 12.f))
#define GasIndexAlgorithm_GATING_MAX_RATIO (0.3f)
#define GasIndexAlgorithm_SIGMOID_L (500.f)
#define GasIndexAlgorithm_SIGMOID_K_VOC (-0.0065f)
#define GasIndexAlgorithm_SIGMOID_X0_VOC (213.f)
#define GasIndexAlgorithm_SIGMOID_K_NOX (-0.0101f)
#define GasIndexAlgorithm_SIGMOID_X0_NOX (614.f)
#define GasIndexAlgorithm_VOC_INDEX_OFFSET_DEFAULT (100.f)
#define GasIndexAlgorithm_NOX_INDEX_OFFSET_DEFAULT (1.f)
#define GasIndexAlgorithm_LP_TAU_FAST (20.0f)
#define GasIndexAlgorithm_LP_TAU_SLOW (500.0f)
#define GasIndexAlgorithm_LP_ALPHA (-0.2f)
#define GasIndexAlgorithm_VOC_SRAW_MINIMUM (20000)
#define GasIndexAlgorithm_NOX_SRAW_MINIMUM (10000)
#define GasIndexAlgorithm_PERSISTENCE_UPTIME_GAMMA ((3.f * 3600.f))
#define GasIndexAlgorithm_TUNING_INDEX_OFFSET_MIN (1)
#define GasIndexAlgorithm_TUNING_INDEX_OFFSET_MAX (250)
#define GasIndexAlgorithm_TUNING_LEARNING_TIME_OFFSET_HOURS_MIN (1)
#define GasIndexAlgorithm_TUNING_LEARNING_TIME_OFFSET_HOURS_MAX (1000)
#define GasIndexAlgorithm_TUNING_LEARNING_TIME_GAIN_HOURS_MIN (1)
#define GasIndexAlgorithm_TUNING_LEARNING_TIME_GAIN_HOURS_MAX (1000)
#define GasIndexAlgorithm_TUNING_GATING_MAX_DURATION_MINUTES_MIN (0)
#define GasIndexAlgorithm_TUNING_GATING_MAX_DURATION_MINUTES_MAX (3000)
#define GasIndexAlgorithm_TUNING_STD_INITIAL_MIN (10)
#define GasIndexAlgorithm_TUNING_STD_INITIAL_MAX (5000)
#define GasIndexAlgorithm_TUNING_GAIN_FACTOR_MIN (1)
#define GasIndexAlgorithm_TUNING_GAIN_FACTOR_MAX (1000)
#define GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING (64.f)
#define GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__ADDITIONAL_GAMMA_MEAN_SCALING \
(8.f)
#define GasIndexAlgorithm_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX (32767.f)
/**
* Struct to hold all parameters and states of the gas algorithm.
*/
typedef struct {
int mAlgorithm_Type;
float mSamplingInterval;
float mIndex_Offset;
int32_t mSraw_Minimum;
float mGating_Max_Duration_Minutes;
float mInit_Duration_Mean;
float mInit_Duration_Variance;
float mGating_Threshold;
float mIndex_Gain;
float mTau_Mean_Hours;
float mTau_Variance_Hours;
float mSraw_Std_Initial;
float mUptime;
float mSraw;
float mGas_Index;
bool m_Mean_Variance_Estimator___Initialized;
float m_Mean_Variance_Estimator___Mean;
float m_Mean_Variance_Estimator___Sraw_Offset;
float m_Mean_Variance_Estimator___Std;
float m_Mean_Variance_Estimator___Gamma_Mean;
float m_Mean_Variance_Estimator___Gamma_Variance;
float m_Mean_Variance_Estimator___Gamma_Initial_Mean;
float m_Mean_Variance_Estimator___Gamma_Initial_Variance;
float m_Mean_Variance_Estimator__Gamma_Mean;
float m_Mean_Variance_Estimator__Gamma_Variance;
float m_Mean_Variance_Estimator___Uptime_Gamma;
float m_Mean_Variance_Estimator___Uptime_Gating;
float m_Mean_Variance_Estimator___Gating_Duration_Minutes;
float m_Mean_Variance_Estimator___Sigmoid__K;
float m_Mean_Variance_Estimator___Sigmoid__X0;
float m_Mox_Model__Sraw_Std;
float m_Mox_Model__Sraw_Mean;
float m_Sigmoid_Scaled__K;
float m_Sigmoid_Scaled__X0;
float m_Sigmoid_Scaled__Offset_Default;
float m_Adaptive_Lowpass__A1;
float m_Adaptive_Lowpass__A2;
bool m_Adaptive_Lowpass___Initialized;
float m_Adaptive_Lowpass___X1;
float m_Adaptive_Lowpass___X2;
float m_Adaptive_Lowpass___X3;
} GasIndexAlgorithmParams;
/**
* Initialize the gas index algorithm parameters for the specified algorithm
* type and reset its internal states. Call this once at the beginning.
* @param params Pointer to the GasIndexAlgorithmParams struct
* @param algorithm_type 0 (GasIndexAlgorithm_ALGORITHM_TYPE_VOC) for VOC or
* 1 (GasIndexAlgorithm_ALGORITHM_TYPE_NOX) for NOx
*/
void GasIndexAlgorithm_init(GasIndexAlgorithmParams* params,
int32_t algorithm_type);
/**
* Initialize the gas index algorithm parameters for the specified algorithm
* type and reset its internal states. Call this once at the beginning.
* @param params Pointer to the GasIndexAlgorithmParams struct
* @param algorithm_type 0 (GasIndexAlgorithm_ALGORITHM_TYPE_VOC) for VOC or
* 1 (GasIndexAlgorithm_ALGORITHM_TYPE_NOX) for NOx
* @param sampling_interval The sampling interval in seconds the algorithm is
* called. Tested for 1s and 10s.
*/
void GasIndexAlgorithm_init_with_sampling_interval(
GasIndexAlgorithmParams* params, int32_t algorithm_type,
float sampling_interval);
/**
* Reset the internal states of the gas index algorithm. Previously set tuning
* parameters are preserved. Call this when resuming operation after a
* measurement interruption.
* @param params Pointer to the GasIndexAlgorithmParams struct
*/
void GasIndexAlgorithm_reset(GasIndexAlgorithmParams* params);
/**
* Get current algorithm states. Retrieved values can be used in
* GasIndexAlgorithm_set_states() to resume operation after a short
* interruption, skipping initial learning phase.
* NOTE: This feature can only be used for VOC algorithm type and after at least
* 3 hours of continuous operation.
* @param params Pointer to the GasIndexAlgorithmParams struct
* @param state0 State0 to be stored
* @param state1 State1 to be stored
*/
void GasIndexAlgorithm_get_states(const GasIndexAlgorithmParams* params,
float* state0, float* state1);
/**
* Set previously retrieved algorithm states to resume operation after a short
* interruption, skipping initial learning phase. This feature should not be
* used after interruptions of more than 10 minutes. Call this once after
* GasIndexAlgorithm_init() or GasIndexAlgorithm_reset() and the optional
* GasIndexAlgorithm_set_tuning_parameters(), if desired. Otherwise, the
* algorithm will start with initial learning phase.
* NOTE: This feature can only be used for VOC algorithm type.
* @param params Pointer to the GasIndexAlgorithmParams struct
* @param state0 State0 to be restored
* @param state1 State1 to be restored
*/
void GasIndexAlgorithm_set_states(GasIndexAlgorithmParams* params, float state0,
float state1);
/**
* Set parameters to customize the gas index algorithm. Call this once after
* GasIndexAlgorithm_init() and before optional GasIndexAlgorithm_set_states(),
* if desired. Otherwise, the default values will be used.
*
* @param params Pointer to the GasIndexAlgorithmParams
* struct
* @param index_offset Gas index representing typical (average)
* conditions. Range 1..250,
* default 100 for VOC and 1 for NOx
* @param learning_time_offset_hours Time constant of long-term estimator for
* offset. Past events will be forgotten
* after about twice the learning time.
* Range 1..1000 [hours], default 12 [hours]
* @param learning_time_gain_hours Time constant of long-term estimator for
* gain. Past events will be forgotten
* after about twice the learning time.
* Range 1..1000 [hours], default 12 [hours]
* NOTE: This value is not relevant for NOx
* algorithm type
* @param gating_max_duration_minutes Maximum duration of gating (freeze of
* estimator during high gas index signal).
* 0 (no gating) or range 1..3000 [minutes],
* default 180 [minutes] for VOC and
* 720 [minutes] for NOx
* @param std_initial Initial estimate for standard deviation.
* Lower value boosts events during initial
* learning period, but may result in larger
* device-to-device variations.
* Range 10..5000, default 50
* NOTE: This value is not relevant for NOx
* algorithm type
* @param gain_factor Factor used to scale applied gain value
* when calculating gas index. Range 1..1000,
* default 230
*/
void GasIndexAlgorithm_set_tuning_parameters(
GasIndexAlgorithmParams* params, int32_t index_offset,
int32_t learning_time_offset_hours, int32_t learning_time_gain_hours,
int32_t gating_max_duration_minutes, int32_t std_initial,
int32_t gain_factor);
/**
* Get current parameters to customize the gas index algorithm.
* Refer to GasIndexAlgorithm_set_tuning_parameters() for description of the
* parameters.
*/
void GasIndexAlgorithm_get_tuning_parameters(
const GasIndexAlgorithmParams* params, int32_t* index_offset,
int32_t* learning_time_offset_hours, int32_t* learning_time_gain_hours,
int32_t* gating_max_duration_minutes, int32_t* std_initial,
int32_t* gain_factor);
/**
* Get the sampling interval parameter used by the algorithm.
*/
void GasIndexAlgorithm_get_sampling_interval(
const GasIndexAlgorithmParams* params, float* sampling_interval);
/**
* Calculate the gas index value from the raw sensor value.
*
* @param params Pointer to the GasIndexAlgorithmParams struct
* @param sraw Raw value from the SGP4x sensor
* @param gas_index Calculated gas index value from the raw sensor value. Zero
* during initial blackout period and 1..500 afterwards
*/
void GasIndexAlgorithm_process(GasIndexAlgorithmParams* params, int32_t sraw,
int32_t* gas_index);
#endif /* GASINDEXALGORITHM_H_ */

View File

@ -111,18 +111,6 @@ void WiFiClass32::scrubDNS(void) {
} else { } else {
dns_setserver(i, IP4_ADDR_ANY); dns_setserver(i, IP4_ADDR_ANY);
} }
#else // USE_IPV6
uint32_t ip_dns = ip_addr_get_ip4_u32(dns_getserver(i));
// Step 1. save valid values from DNS
if (has_v4 && (uint32_t)ip_dns != 0) {
ip_addr_set_ip4_u32_val(dns_save4[i], ip_dns);
}
// Step 2. scrub addresses not supported
if (!has_v4) {
ip_addr_set_ip4_u32_val(dns_save4[i], 0L);
}
// Step 3. restore saved value
dns_setserver(i, &dns_save4[i]);
#endif // USE_IPV6 #endif // USE_IPV6
} }
// AddLog(LOG_LEVEL_DEBUG, "IP>: DNS: from(%s %s) to (%s %s) has4/6:%i-%i", dns_entry0.c_str(), dns_entry1.c_str(), IPAddress(dns_getserver(0)).toString().c_str(), IPAddress(dns_getserver(1)).toString().c_str(), has_v4, has_v6); // AddLog(LOG_LEVEL_DEBUG, "IP>: DNS: from(%s %s) to (%s %s) has4/6:%i-%i", dns_entry0.c_str(), dns_entry1.c_str(), IPAddress(dns_getserver(0)).toString().c_str(), IPAddress(dns_getserver(1)).toString().c_str(), has_v4, has_v6);

View File

@ -76,8 +76,8 @@ public:
void scrubDNS(void); void scrubDNS(void);
protected: protected:
ip_addr_t dns_save4[DNS_MAX_SERVERS] = {}; // IPv4 DNS servers
#ifdef USE_IPV6 #ifdef USE_IPV6
ip_addr_t dns_save4[DNS_MAX_SERVERS] = {}; // IPv4 DNS servers
ip_addr_t dns_save6[DNS_MAX_SERVERS] = {}; // IPv6 DNS servers ip_addr_t dns_save6[DNS_MAX_SERVERS] = {}; // IPv6 DNS servers
#endif // USE_IPV6 #endif // USE_IPV6
}; };

View File

@ -16,6 +16,7 @@
#ifdef ESP32 #ifdef ESP32
#include "Arduino.h" #include "Arduino.h"
#include "esp_idf_version.h"
#include "esp8266toEsp32.h" #include "esp8266toEsp32.h"
#include "driver/ledc.h" #include "driver/ledc.h"
@ -33,7 +34,11 @@ enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_D
#else #else
#define LEDC_DEFAULT_CLK LEDC_AUTO_CLK #define LEDC_DEFAULT_CLK LEDC_AUTO_CLK
#endif #endif
#define LEDC_MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM #if (ESP_IDF_VERSION_MAJOR >= 5)
#define LEDC_MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDTH
#else
#define LEDC_MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM
#endif
// define our limits to ease any change from esp-idf // define our limits to ease any change from esp-idf
#define MAX_TIMERS LEDC_TIMER_MAX // 4 timers for all ESP32 variants #define MAX_TIMERS LEDC_TIMER_MAX // 4 timers for all ESP32 variants

View File

@ -53,7 +53,7 @@ uint8_t ledcReadResolution(uint8_t chan);
// was not yet attached. // was not yet attached.
// //
// Returns: hardware channel number, or -1 if it failed // Returns: hardware channel number, or -1 if it failed
int analogAttach(uint32_t pin, bool output_invert = false); // returns the ledc channel, or -1 if failed. This is implicitly called by analogWrite if the channel was not already allocated int32_t analogAttach(uint32_t pin, bool output_invert = false); // returns the ledc channel, or -1 if failed. This is implicitly called by analogWrite if the channel was not already allocated
// change both freq and range // change both freq and range
// `0`: set to global value // `0`: set to global value

Binary file not shown.

View File

@ -1038,7 +1038,9 @@ BERRY_API int be_pcall(bvm *vm, int argc)
return be_protectedcall(vm, f, argc); return be_protectedcall(vm, f, argc);
} }
#ifdef __GNUC__
__attribute__((noreturn)) __attribute__((noreturn))
#endif
BERRY_API void be_raise(bvm *vm, const char *except, const char *msg) BERRY_API void be_raise(bvm *vm, const char *except, const char *msg)
{ {
be_pushstring(vm, except); be_pushstring(vm, except);

View File

@ -356,6 +356,40 @@ static uint16_t buf_get2_be(buf_impl* attr, size_t offset)
return 0; return 0;
} }
static uint32_t buf_get3_le(buf_impl* attr, size_t offset)
{
if ((int32_t)offset + 2 < attr->len) {
return attr->bufptr[offset] | (attr->bufptr[offset+1] << 8) | (attr->bufptr[offset+2] << 16);
}
return 0;
}
static uint16_t buf_get3_be(buf_impl* attr, size_t offset)
{
if ((int32_t)offset + 2 < attr->len) {
return attr->bufptr[offset+2] | (attr->bufptr[offset+1] << 8) | (attr->bufptr[offset] << 16);
}
return 0;
}
static void buf_set3_le(buf_impl* attr, size_t offset, uint32_t data)
{
if ((int32_t)offset + 2 < attr->len) {
attr->bufptr[offset] = data & 0xFF;
attr->bufptr[offset+1] = (data >> 8) & 0xFF;
attr->bufptr[offset+2] = (data >> 16) & 0xFF;
}
}
static void buf_set3_be(buf_impl* attr, size_t offset, uint32_t data)
{
if ((int32_t)offset + 2 < attr->len) {
attr->bufptr[offset+2] = data & 0xFF;
attr->bufptr[offset+1] = (data >> 8) & 0xFF;
attr->bufptr[offset] = (data >> 16) & 0xFF;
}
}
static void buf_set4_le(buf_impl* attr, size_t offset, uint32_t data) static void buf_set4_le(buf_impl* attr, size_t offset, uint32_t data)
{ {
if ((int32_t)offset + 3 < attr->len) { if ((int32_t)offset + 3 < attr->len) {
@ -832,12 +866,18 @@ static int m_get(bvm *vm, bbool sign)
case 2: ret = buf_get2_le(&attr, idx); case 2: ret = buf_get2_le(&attr, idx);
if (sign) { ret = (int16_t)(uint16_t) ret; } if (sign) { ret = (int16_t)(uint16_t) ret; }
break; break;
case 3: ret = buf_get3_le(&attr, idx);
if (sign & (ret & 0x800000)) { ret = ret | 0xFF000000; }
break;
case 4: ret = buf_get4_le(&attr, idx); break; case 4: ret = buf_get4_le(&attr, idx); break;
case -2: ret = buf_get2_be(&attr, idx); case -2: ret = buf_get2_be(&attr, idx);
if (sign) { ret = (int16_t)(uint16_t) ret; } if (sign) { ret = (int16_t)(uint16_t) ret; }
break; break;
case -3: ret = buf_get3_be(&attr, idx);
if (sign & (ret & 0x800000)) { ret = ret | 0xFF000000; }
break;
case -4: ret = buf_get4_be(&attr, idx); break; case -4: ret = buf_get4_be(&attr, idx); break;
default: be_raise(vm, "type_error", "size must be -4, -2, -1, 0, 1, 2 or 4."); default: be_raise(vm, "type_error", "size must be -4, -3, -2, -1, 0, 1, 2, 3 or 4.");
} }
be_pop(vm, argc - 1); be_pop(vm, argc - 1);
if (vsize != 0) { if (vsize != 0) {
@ -911,10 +951,12 @@ static int m_set(bvm *vm)
case -1: /* fallback below */ case -1: /* fallback below */
case 1: buf_set1(&attr, idx, value); break; case 1: buf_set1(&attr, idx, value); break;
case 2: buf_set2_le(&attr, idx, value); break; case 2: buf_set2_le(&attr, idx, value); break;
case 3: buf_set3_le(&attr, idx, value); break;
case 4: buf_set4_le(&attr, idx, value); break; case 4: buf_set4_le(&attr, idx, value); break;
case -2: buf_set2_be(&attr, idx, value); break; case -2: buf_set2_be(&attr, idx, value); break;
case -3: buf_set3_be(&attr, idx, value); break;
case -4: buf_set4_be(&attr, idx, value); break; case -4: buf_set4_be(&attr, idx, value); break;
default: be_raise(vm, "type_error", "size must be -4, -2, -1, 0, 1, 2 or 4."); default: be_raise(vm, "type_error", "size must be -4, -3, -2, -1, 0, 1, 2, 3 or 4.");
} }
be_pop(vm, argc - 1); be_pop(vm, argc - 1);
m_write_attributes(vm, 1, &attr); /* update attributes */ m_write_attributes(vm, 1, &attr); /* update attributes */

View File

@ -78,20 +78,11 @@ static int codeABx(bfuncinfo *finfo, bopcode op, int a, int bx)
return codeinst(finfo, ISET_OP(op) | ISET_RA(a) | ISET_Bx(bx)); return codeinst(finfo, ISET_OP(op) | ISET_RA(a) | ISET_Bx(bx));
} }
/* Move value from register b to register a */
static void code_move_nooptim(bfuncinfo *finfo, int a, int b)
{
if (isK(b)) {
codeABx(finfo, OP_LDCONST, a, b & 0xFF);
} else {
codeABC(finfo, OP_MOVE, a, b, 0);
}
}
/* Move value from register b to register a */ /* Move value from register b to register a */
/* Check the previous instruction to compact both instruction as one if possible */ /* Check the previous instruction to compact both instruction as one if possible */
/* If b is a constant, add LDCONST or add MOVE otherwise */ /* If b is a constant, add LDCONST or add MOVE otherwise */
static void code_move(bfuncinfo *finfo, int a, int b) /* returns false if the move operation happened, or true if there was a register optimization and `b` should be replaced by `a` */
static bbool code_move(bfuncinfo *finfo, int a, int b)
{ {
if (finfo->pc) { /* If not the first instruction of the function */ if (finfo->pc) { /* If not the first instruction of the function */
binstruction *i = be_vector_end(&finfo->code); /* get the last instruction */ binstruction *i = be_vector_end(&finfo->code); /* get the last instruction */
@ -101,11 +92,23 @@ static void code_move(bfuncinfo *finfo, int a, int b)
int x = IGET_RA(*i), y = IGET_RKB(*i), z = IGET_RKC(*i); int x = IGET_RA(*i), y = IGET_RKB(*i), z = IGET_RKC(*i);
if (b == x && (a == y || (op < OP_NEG && a == z))) { if (b == x && (a == y || (op < OP_NEG && a == z))) {
*i = (*i & ~IRA_MASK) | ISET_RA(a); *i = (*i & ~IRA_MASK) | ISET_RA(a);
return; return btrue;
}
}
if (!isK(b)) { /* OP_MOVE */
/* check if the previous OP_MOVE is not identical */
binstruction mov = ISET_OP(OP_MOVE) | ISET_RA(a) | ISET_RKB(b) | ISET_RKC(0);
if (mov == *i) {
return btrue; /* previous instruction is the same move, remove duplicate */
} }
} }
} }
code_move_nooptim(finfo, a, b); if (isK(b)) {
codeABx(finfo, OP_LDCONST, a, b & 0xFF);
} else {
codeABC(finfo, OP_MOVE, a, b, 0);
}
return bfalse;
} }
/* Free register at top (checks that it´s a register) */ /* Free register at top (checks that it´s a register) */
@ -113,7 +116,7 @@ static void code_move(bfuncinfo *finfo, int a, int b)
static void free_expreg(bfuncinfo *finfo, bexpdesc *e) static void free_expreg(bfuncinfo *finfo, bexpdesc *e)
{ {
/* release temporary register */ /* release temporary register */
if (e && e->type == ETREG) { if (e && e->type == ETREG && e->v.idx == finfo->freereg - 1) { /* free ETREG only if it's top of stack */
be_code_freeregs(finfo, 1); be_code_freeregs(finfo, 1);
} }
} }
@ -690,10 +693,10 @@ int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, bbool keep_reg)
switch (e1->type) { switch (e1->type) {
case ETLOCAL: /* It can't be ETREG. */ case ETLOCAL: /* It can't be ETREG. */
if (e1->v.idx != src) { if (e1->v.idx != src) {
if (keep_reg) { bbool reg_optimized = code_move(finfo, e1->v.idx, src); /* do explicit move only if needed */
code_move_nooptim(finfo, e1->v.idx, src); /* always do explicit move */ if (reg_optimized) {
} else { free_expreg(finfo, e2); /* free source (checks only ETREG) */
code_move(finfo, e1->v.idx, src); /* do explicit move only if needed */ *e2 = *e1; /* now e2 is e1 ETLOCAL */
} }
} }
break; break;
@ -725,7 +728,7 @@ int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e)
{ {
int dst = finfo->freereg; int dst = finfo->freereg;
int src = exp2anyreg(finfo, e); /* get variable register index */ int src = exp2anyreg(finfo, e); /* get variable register index */
if (e->type != ETREG) { /* move local and const to new register */ if ((e->type != ETREG) || (src < dst - 1)) { /* move local and const to new register, don't move if already top of stack */
code_move(finfo, dst, src); code_move(finfo, dst, src);
be_code_allocregs(finfo, 1); be_code_allocregs(finfo, 1);
} else { } else {

View File

@ -167,14 +167,45 @@ static int m_counters(bvm *vm)
map_insert(vm, "call", vm->counter_call); map_insert(vm, "call", vm->counter_call);
map_insert(vm, "get", vm->counter_get); map_insert(vm, "get", vm->counter_get);
map_insert(vm, "set", vm->counter_set); map_insert(vm, "set", vm->counter_set);
map_insert(vm, "getgbl", vm->counter_get_global);
map_insert(vm, "try", vm->counter_try); map_insert(vm, "try", vm->counter_try);
map_insert(vm, "raise", vm->counter_exc); map_insert(vm, "raise", vm->counter_exc);
map_insert(vm, "objects", vm->counter_gc_kept); map_insert(vm, "objects", vm->counter_gc_kept);
map_insert(vm, "mem_alloc", vm->counter_mem_alloc);
map_insert(vm, "mem_free", vm->counter_mem_free);
map_insert(vm, "mem_realloc", vm->counter_mem_realloc);
be_pop(vm, 1); be_pop(vm, 1);
be_return(vm); be_return(vm);
} }
#endif #endif
static int m_allocs(bvm *vm) {
#if BE_USE_PERF_COUNTERS
be_pushint(vm, vm->counter_mem_alloc);
be_return(vm);
#else
be_return_nil(vm);
#endif
}
static int m_frees(bvm *vm) {
#if BE_USE_PERF_COUNTERS
be_pushint(vm, vm->counter_mem_free);
be_return(vm);
#else
be_return_nil(vm);
#endif
}
static int m_reallocs(bvm *vm) {
#if BE_USE_PERF_COUNTERS
be_pushint(vm, vm->counter_mem_realloc);
be_return(vm);
#else
be_return_nil(vm);
#endif
}
#if !BE_USE_PRECOMPILED_OBJECT #if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(debug) { be_native_module_attr_table(debug) {
be_native_module_function("attrdump", m_attrdump), be_native_module_function("attrdump", m_attrdump),
@ -207,6 +238,10 @@ module debug (scope: global, depend: BE_USE_DEBUG_MODULE) {
top, func(m_top) top, func(m_top)
varname, func(m_varname), BE_DEBUG_VAR_INFO varname, func(m_varname), BE_DEBUG_VAR_INFO
upvname, func(m_upvname), BE_DEBUG_VAR_INFO upvname, func(m_upvname), BE_DEBUG_VAR_INFO
// individual counters
allocs, func(m_allocs)
frees, func(m_frees)
reallocs, func(m_reallocs)
} }
@const_object_info_end */ @const_object_info_end */
#include "../generate/be_fixed_debug.h" #include "../generate/be_fixed_debug.h"

View File

@ -41,7 +41,7 @@ static const char* const kwords_tab[] = {
"for", "def", "end", "class", "break", "continue", "for", "def", "end", "class", "break", "continue",
"return", "true", "false", "nil", "var", "do", "return", "true", "false", "nil", "var", "do",
"import", "as", "try", "except", "raise", "static", "import", "as", "try", "except", "raise", "static",
// ".f" ":=",
}; };
void be_lexerror(blexer *lexer, const char *msg) void be_lexerror(blexer *lexer, const char *msg)

View File

@ -262,6 +262,14 @@ static int m_size(bvm *vm)
be_return(vm); be_return(vm);
} }
static int m_tobool(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 1);
be_pushbool(vm, be_data_size(vm, -1) > 0);
be_return(vm);
}
static int m_resize(bvm *vm) static int m_resize(bvm *vm)
{ {
be_getmember(vm, 1, ".p"); be_getmember(vm, 1, ".p");
@ -507,6 +515,7 @@ void be_load_listlib(bvm *vm)
{ "reverse", m_reverse }, { "reverse", m_reverse },
{ "copy", m_copy }, { "copy", m_copy },
{ "keys", m_keys }, { "keys", m_keys },
{ "tobool", m_tobool }
{ "..", m_connect }, { "..", m_connect },
{ "+", m_merge }, { "+", m_merge },
{ "==", m_equal }, { "==", m_equal },
@ -536,6 +545,7 @@ class be_class_list (scope: global, name: list) {
reverse, func(m_reverse) reverse, func(m_reverse)
copy, func(m_copy) copy, func(m_copy)
keys, func(m_keys) keys, func(m_keys)
tobool, func(m_tobool)
.., func(m_connect) .., func(m_connect)
+, func(m_merge) +, func(m_merge)
==, func(m_equal) ==, func(m_equal)

View File

@ -150,6 +150,14 @@ static int m_size(bvm *vm)
be_return(vm); be_return(vm);
} }
static int m_tobool(bvm *vm)
{
be_getmember(vm, 1, ".p");
map_check_data(vm, 1);
be_pushbool(vm, be_data_size(vm, -1) > 0);
be_return(vm);
}
static int iter_closure(bvm *vm) static int iter_closure(bvm *vm)
{ {
/* for better performance, we operate the upvalues /* for better performance, we operate the upvalues
@ -229,6 +237,7 @@ void be_load_maplib(bvm *vm)
{ "insert", m_insert }, { "insert", m_insert },
{ "iter", m_iter }, { "iter", m_iter },
{ "keys", m_keys }, { "keys", m_keys },
{ "tobool", m_tobool }
{ NULL, NULL } { NULL, NULL }
}; };
be_regclass(vm, "map", members); be_regclass(vm, "map", members);
@ -249,6 +258,7 @@ class be_class_map (scope: global, name: map) {
insert, func(m_insert) insert, func(m_insert)
iter, func(m_iter) iter, func(m_iter)
keys, func(m_keys) keys, func(m_keys)
tobool, func(m_tobool)
} }
@const_object_info_end */ @const_object_info_end */
#include "../generate/be_fixed_be_class_map.h" #include "../generate/be_fixed_be_class_map.h"

View File

@ -60,12 +60,18 @@ BERRY_API void* be_realloc(bvm *vm, void *ptr, size_t old_size, size_t new_size)
while (1) { while (1) {
/* Case 1: new allocation */ /* Case 1: new allocation */
#if BE_USE_PERF_COUNTERS
vm->counter_mem_alloc++;
#endif
if (!ptr || (old_size == 0)) { if (!ptr || (old_size == 0)) {
block = malloc_from_pool(vm, new_size); block = malloc_from_pool(vm, new_size);
} }
/* Case 2: deallocate */ /* Case 2: deallocate */
else if (new_size == 0) { else if (new_size == 0) {
#if BE_USE_PERF_COUNTERS
vm->counter_mem_free++;
#endif
if (ptr == NULL) { return NULL; } /* safeguard */ if (ptr == NULL) { return NULL; } /* safeguard */
#if BE_USE_DEBUG_GC #if BE_USE_DEBUG_GC
memset(ptr, 0xFF, old_size); /* fill the structure with invalid pointers */ memset(ptr, 0xFF, old_size); /* fill the structure with invalid pointers */
@ -76,6 +82,9 @@ BERRY_API void* be_realloc(bvm *vm, void *ptr, size_t old_size, size_t new_size)
/* Case 3: reallocate with a different size */ /* Case 3: reallocate with a different size */
else if (new_size && old_size) { // TODO we already know they are not null TODO else if (new_size && old_size) { // TODO we already know they are not null TODO
#if BE_USE_PERF_COUNTERS
vm->counter_mem_realloc++;
#endif
if (new_size <= POOL32_SIZE || old_size <=POOL32_SIZE) { if (new_size <= POOL32_SIZE || old_size <=POOL32_SIZE) {
/* complex case with different pools */ /* complex case with different pools */
if (new_size <= POOL16_SIZE && old_size <= POOL16_SIZE) { if (new_size <= POOL16_SIZE && old_size <= POOL16_SIZE) {

View File

@ -196,6 +196,7 @@ static void begin_block(bfuncinfo *finfo, bblockinfo *binfo, int type)
finfo->binfo = binfo; /* tell parser this is the current block */ finfo->binfo = binfo; /* tell parser this is the current block */
binfo->type = (bbyte)type; binfo->type = (bbyte)type;
binfo->hasupval = 0; binfo->hasupval = 0;
binfo->sideeffect = 0;
binfo->beginpc = finfo->pc; /* set starting pc for this block */ binfo->beginpc = finfo->pc; /* set starting pc for this block */
binfo->nactlocals = (bbyte)be_list_count(finfo->local); /* count number of local variables in previous block */ binfo->nactlocals = (bbyte)be_list_count(finfo->local); /* count number of local variables in previous block */
if (type & BLOCK_LOOP) { if (type & BLOCK_LOOP) {
@ -796,6 +797,7 @@ static void call_expr(bparser *parser, bexpdesc *e)
int argc = 0, base; int argc = 0, base;
int ismember = e->type == ETMEMBER; int ismember = e->type == ETMEMBER;
parser->finfo->binfo->sideeffect = 1; /* has side effect */
/* func '(' [exprlist] ')' */ /* func '(' [exprlist] ')' */
check_var(parser, e); check_var(parser, e);
/* code function index to next register */ /* code function index to next register */
@ -1030,11 +1032,13 @@ static void assign_expr(bparser *parser)
bexpdesc e; bexpdesc e;
btokentype op; btokentype op;
int line = parser->lexer.linenumber; int line = parser->lexer.linenumber;
parser->finfo->binfo->sideeffect = 0; /* reinit side effect marker */
expr(parser, &e); /* left expression */ expr(parser, &e); /* left expression */
check_symbol(parser, &e); check_symbol(parser, &e);
op = get_assign_op(parser); op = get_assign_op(parser);
if (op != OP_NOT_ASSIGN) { /* assign operator */ if (op != OP_NOT_ASSIGN) { /* assign operator */
bexpdesc e1; bexpdesc e1;
parser->finfo->binfo->sideeffect = 1;
scan_next_token(parser); scan_next_token(parser);
compound_assign(parser, op, &e, &e1); compound_assign(parser, op, &e, &e1);
if (check_newvar(parser, &e)) { /* new variable */ if (check_newvar(parser, &e)) { /* new variable */
@ -1110,6 +1114,9 @@ static void sub_expr(bparser *parser, bexpdesc *e, int prio)
check_var(parser, e); /* check that left part is valid */ check_var(parser, e); /* check that left part is valid */
scan_next_token(parser); /* move to next token */ scan_next_token(parser); /* move to next token */
be_code_prebinop(finfo, op, e); /* and or */ be_code_prebinop(finfo, op, e); /* and or */
if (op == OptConnect) {
parser->finfo->binfo->sideeffect = 1;
}
init_exp(&e2, ETVOID, 0); init_exp(&e2, ETVOID, 0);
sub_expr(parser, &e2, binary_op_prio(op)); /* parse right side */ sub_expr(parser, &e2, binary_op_prio(op)); /* parse right side */
if ((e2.type == ETVOID) && (op == OptConnect)) { if ((e2.type == ETVOID) && (op == OptConnect)) {
@ -1131,9 +1138,12 @@ static void walrus_expr(bparser *parser, bexpdesc *e)
sub_expr(parser, e, ASSIGN_OP_PRIO); /* left expression */ sub_expr(parser, e, ASSIGN_OP_PRIO); /* left expression */
btokentype op = next_type(parser); btokentype op = next_type(parser);
if (op == OptWalrus) { if (op == OptWalrus) {
check_symbol(parser, e);
bexpdesc e1 = *e; /* copy var to e1, e will get the result of expression */ bexpdesc e1 = *e; /* copy var to e1, e will get the result of expression */
parser->finfo->binfo->sideeffect = 1; /* has side effect */
scan_next_token(parser); /* skip ':=' */ scan_next_token(parser); /* skip ':=' */
expr(parser, e); expr(parser, e);
check_var(parser, e);
if (check_newvar(parser, &e1)) { /* new variable */ if (check_newvar(parser, &e1)) { /* new variable */
new_var(parser, e1.v.s, e); new_var(parser, e1.v.s, e);
} }
@ -1556,7 +1566,25 @@ static void class_stmt(bparser *parser)
new_var(parser, name, &e); new_var(parser, name, &e);
be_code_class(parser->finfo, &e, c); be_code_class(parser->finfo, &e, c);
class_inherit(parser, &e); class_inherit(parser, &e);
bblockinfo binfo;
begin_block(parser->finfo, &binfo, 0);
bstring *class_str = parser_newstr(parser, "_class"); /* we always define `_class` local variable */
if (e.type == ETLOCAL) {
bexpdesc e1; /* if inline class, we add a second local variable for _class */
init_exp(&e1, ETLOCAL, 0);
e1.v.idx = new_localvar(parser, class_str);
be_code_setvar(parser->finfo, &e1, &e, 1);
} else { /* if global class, we just reuse the newly created class in the register */
init_exp(&e, ETLOCAL, 0);
e.v.idx = new_localvar(parser, class_str);
}
begin_varinfo(parser, class_str);
class_block(parser, c, &e); class_block(parser, c, &e);
end_block(parser);
be_class_compress(parser->vm, c); /* compress class size */ be_class_compress(parser->vm, c); /* compress class size */
match_token(parser, KeyEnd); /* skip 'end' */ match_token(parser, KeyEnd); /* skip 'end' */
} else { } else {
@ -1756,6 +1784,9 @@ static void throw_stmt(bparser *parser)
static void statement(bparser *parser) static void statement(bparser *parser)
{ {
/* save value of sideeffect */
bbyte sideeffect = parser->finfo->binfo->sideeffect;
parser->finfo->binfo->sideeffect = 1; /* by default declare side effect */
switch (next_type(parser)) { switch (next_type(parser)) {
case KeyIf: if_stmt(parser); break; case KeyIf: if_stmt(parser); break;
case KeyWhile: while_stmt(parser); break; case KeyWhile: while_stmt(parser); break;
@ -1770,8 +1801,16 @@ static void statement(bparser *parser)
case KeyVar: var_stmt(parser); break; case KeyVar: var_stmt(parser); break;
case KeyTry: try_stmt(parser); break; case KeyTry: try_stmt(parser); break;
case KeyRaise: throw_stmt(parser); break; case KeyRaise: throw_stmt(parser); break;
case OptSemic: scan_next_token(parser); break; /* empty statement */ case OptSemic:
default: expr_stmt(parser); break; parser->finfo->binfo->sideeffect = sideeffect; /* restore sideeffect */
scan_next_token(parser); break; /* empty statement */
default:
parser->finfo->binfo->sideeffect = sideeffect; /* restore sideeffect */
expr_stmt(parser);
if (comp_is_strict(parser->vm) && parser->finfo->binfo->sideeffect == 0) {
push_error(parser, "strict: expression without side effect detected");
}
break;
} }
be_assert(parser->finfo->freereg >= be_list_count(parser->finfo->local)); be_assert(parser->finfo->freereg >= be_list_count(parser->finfo->local));
} }

View File

@ -53,6 +53,7 @@ typedef struct bblockinfo {
bbyte nactlocals; /* number of active local variables */ bbyte nactlocals; /* number of active local variables */
bbyte type; /* block type mask */ bbyte type; /* block type mask */
bbyte hasupval; /* has upvalue mark */ bbyte hasupval; /* has upvalue mark */
bbyte sideeffect; /* did the last expr/statement had a side effect */
int breaklist; /* break list */ int breaklist; /* break list */
int beginpc; /* begin pc */ int beginpc; /* begin pc */
int continuelist; /* continue list */ int continuelist; /* continue list */

View File

@ -11,31 +11,70 @@
static int m_init(bvm *vm) static int m_init(bvm *vm)
{ {
int argc = be_top(vm);
if (argc < 3) { be_raise(vm, "value_error", "missing arguments"); }
if (!be_isint(vm, 2) || !be_isint(vm, 3)) { be_raise(vm, "value_error", "arguments must be 'int'"); }
be_pushvalue(vm, 2); be_pushvalue(vm, 2);
be_setmember(vm, 1, "__lower__"); be_setmember(vm, 1, "__lower__");
be_pop(vm, 1); be_pop(vm, 1);
be_pushvalue(vm, 3); be_pushvalue(vm, 3);
be_setmember(vm, 1, "__upper__"); be_setmember(vm, 1, "__upper__");
int incr = 1; /* default increment is '1' */
if (argc >= 4) {
if (!be_isint(vm, 4)) { be_raise(vm, "value_error", "arguments must be 'int'"); }
incr = be_toint(vm, 4);
if (incr == 0) { be_raise(vm, "value_error", "increment cannot be zero"); }
}
be_pushint(vm, incr);
be_setmember(vm, 1, "__incr__");
be_return_nil(vm); be_return_nil(vm);
} }
static int m_tostring(bvm *vm) static int m_tostring(bvm *vm)
{ {
be_pushstring(vm, "("); be_getmember(vm, 1, "__incr__");
be_getmember(vm, 1, "__lower__"); int incr = be_toint(vm, -1);
be_tostring(vm, -1);
be_strconcat(vm, -2);
be_pop(vm, 1);
be_pushstring(vm, "..");
be_strconcat(vm, -2);
be_pop(vm, 1);
be_getmember(vm, 1, "__upper__");
be_tostring(vm, -1);
be_strconcat(vm, -2);
be_pop(vm, 1);
be_pushstring(vm, ")");
be_strconcat(vm, -2);
be_pop(vm, 1); be_pop(vm, 1);
if (incr == 1) {
be_pushstring(vm, "(");
be_getmember(vm, 1, "__lower__");
be_tostring(vm, -1);
be_strconcat(vm, -2);
be_pop(vm, 1);
be_pushstring(vm, "..");
be_strconcat(vm, -2);
be_pop(vm, 1);
be_getmember(vm, 1, "__upper__");
be_tostring(vm, -1);
be_strconcat(vm, -2);
be_pop(vm, 1);
be_pushstring(vm, ")");
be_strconcat(vm, -2);
be_pop(vm, 1);
} else {
be_pushstring(vm, "range(");
be_getmember(vm, 1, "__lower__");
be_tostring(vm, -1);
be_strconcat(vm, -2);
be_pop(vm, 1);
be_pushstring(vm, ", ");
be_strconcat(vm, -2);
be_pop(vm, 1);
be_getmember(vm, 1, "__upper__");
be_tostring(vm, -1);
be_strconcat(vm, -2);
be_pop(vm, 1);
be_pushstring(vm, ", ");
be_strconcat(vm, -2);
be_pop(vm, 1);
be_getmember(vm, 1, "__incr__");
be_tostring(vm, -1);
be_strconcat(vm, -2);
be_pop(vm, 1);
be_pushstring(vm, ")");
be_strconcat(vm, -2);
be_pop(vm, 1);
}
be_return(vm); be_return(vm);
} }
@ -51,13 +90,30 @@ static int m_lower(bvm *vm)
be_return(vm); be_return(vm);
} }
static int m_incr(bvm *vm)
{
be_getmember(vm, 1, "__incr__");
be_return(vm);
}
static int m_setrange(bvm *vm) static int m_setrange(bvm *vm)
{ {
int argc = be_top(vm);
if (argc < 3) { be_raise(vm, "value_error", "missing arguments"); }
if (!be_isint(vm, 2) || !be_isint(vm, 3)) { be_raise(vm, "value_error", "arguments must be 'int'"); }
be_pushvalue(vm, 2); be_pushvalue(vm, 2);
be_setmember(vm, 1, "__lower__"); be_setmember(vm, 1, "__lower__");
be_pop(vm, 1); be_pop(vm, 1);
be_pushvalue(vm, 3); be_pushvalue(vm, 3);
be_setmember(vm, 1, "__upper__"); be_setmember(vm, 1, "__upper__");
int incr = 1; /* default increment is '1' */
if (argc >= 4) {
if (!be_isint(vm, 4)) { be_raise(vm, "value_error", "arguments must be 'int'"); }
incr = be_toint(vm, 4);
if (incr == 0) { be_raise(vm, "value_error", "increment cannot be zero"); }
}
be_pushint(vm, incr);
be_setmember(vm, 1, "__incr__");
be_return_nil(vm); be_return_nil(vm);
} }
@ -68,25 +124,30 @@ static int iter_closure(bvm *vm)
bntvclos *func = var_toobj(vm->cf->func); bntvclos *func = var_toobj(vm->cf->func);
bvalue *uv0 = be_ntvclos_upval(func, 0)->value; bvalue *uv0 = be_ntvclos_upval(func, 0)->value;
bvalue *uv1 = be_ntvclos_upval(func, 1)->value; bvalue *uv1 = be_ntvclos_upval(func, 1)->value;
bvalue *uv2 = be_ntvclos_upval(func, 2)->value;
bint lower = var_toint(uv0); /* upvalue[0] => lower */ bint lower = var_toint(uv0); /* upvalue[0] => lower */
bint upper = var_toint(uv1); /* upvalue[1] => upper */ bint upper = var_toint(uv1); /* upvalue[1] => upper */
if (lower > upper) { bint incr = var_toint(uv2); /* upvalue[2] => incr */
if ((incr > 0 && lower > upper) || (incr < 0 && lower < upper)) {
be_stop_iteration(vm); be_stop_iteration(vm);
} }
var_toint(uv0) = lower + 1; /* set upvale[0] */ var_toint(uv0) = lower + incr; /* set upvale[0] */
be_pushint(vm, lower); /* push the return value */ be_pushint(vm, lower); /* push the return value */
be_return(vm); be_return(vm);
} }
static int m_iter(bvm *vm) static int m_iter(bvm *vm)
{ {
be_pushntvclosure(vm, iter_closure, 2); be_pushntvclosure(vm, iter_closure, 3);
be_getmember(vm, 1, "__lower__"); be_getmember(vm, 1, "__lower__");
be_setupval(vm, -2, 0); be_setupval(vm, -2, 0);
be_pop(vm, 1); be_pop(vm, 1);
be_getmember(vm, 1, "__upper__"); be_getmember(vm, 1, "__upper__");
be_setupval(vm, -2, 1); be_setupval(vm, -2, 1);
be_pop(vm, 1); be_pop(vm, 1);
be_getmember(vm, 1, "__incr__");
be_setupval(vm, -2, 2);
be_pop(vm, 1);
be_return(vm); be_return(vm);
} }
@ -96,6 +157,7 @@ void be_load_rangelib(bvm *vm)
static const bnfuncinfo members[] = { static const bnfuncinfo members[] = {
{ "__lower__", NULL }, { "__lower__", NULL },
{ "__upper__", NULL }, { "__upper__", NULL },
{ "__incr__", NULL },
{ "init", m_init }, { "init", m_init },
{ "tostring", m_tostring }, { "tostring", m_tostring },
{ "lower", m_lower }, { "lower", m_lower },
@ -111,10 +173,12 @@ void be_load_rangelib(bvm *vm)
class be_class_range (scope: global, name: range) { class be_class_range (scope: global, name: range) {
__lower__, var __lower__, var
__upper__, var __upper__, var
__incr__, var
init, func(m_init) init, func(m_init)
tostring, func(m_tostring) tostring, func(m_tostring)
lower, func(m_lower) lower, func(m_lower)
upper, func(m_upper) upper, func(m_upper)
incr, func(m_incr)
setrange, func(m_setrange) setrange, func(m_setrange)
iter, func(m_iter) iter, func(m_iter)
} }

View File

@ -38,7 +38,6 @@ void be_vector_remove_end(bvector *vector);
void be_vector_resize(bvm *vm, bvector *vector, int count); void be_vector_resize(bvm *vm, bvector *vector, int count);
void be_vector_clear(bvector *vector); void be_vector_clear(bvector *vector);
void* be_vector_release(bvm *vm, bvector *vector); void* be_vector_release(bvm *vm, bvector *vector);
void* be_vector_release_32(bvm *vm, bvector *vector); /* specialized call for 32 bits aligned accesses */
int be_nextsize(int value); int be_nextsize(int value);
#endif #endif

View File

@ -507,10 +507,14 @@ BERRY_API bvm* be_vm_new(void)
vm->counter_call = 0; vm->counter_call = 0;
vm->counter_get = 0; vm->counter_get = 0;
vm->counter_set = 0; vm->counter_set = 0;
vm->counter_get_global = 0;
vm->counter_try = 0; vm->counter_try = 0;
vm->counter_exc = 0; vm->counter_exc = 0;
vm->counter_gc_kept = 0; vm->counter_gc_kept = 0;
vm->counter_gc_freed = 0; vm->counter_gc_freed = 0;
vm->counter_mem_alloc = 0;
vm->counter_mem_free = 0;
vm->counter_mem_realloc = 0;
#endif #endif
return vm; return vm;
} }
@ -579,6 +583,9 @@ newframe: /* a new call frame */
dispatch(); dispatch();
} }
opcase(GETNGBL): { /* get Global by name */ opcase(GETNGBL): { /* get Global by name */
#if BE_USE_PERF_COUNTERS
vm->counter_get_global++;
#endif
bvalue *v = RA(); bvalue *v = RA();
bvalue *b = RKB(); bvalue *b = RKB();
if (var_isstr(b)) { if (var_isstr(b)) {

View File

@ -114,10 +114,14 @@ struct bvm {
uint32_t counter_call; /* counter for calls, VM or native */ uint32_t counter_call; /* counter for calls, VM or native */
uint32_t counter_get; /* counter for GETMBR or GETMET */ uint32_t counter_get; /* counter for GETMBR or GETMET */
uint32_t counter_set; /* counter for SETMBR */ uint32_t counter_set; /* counter for SETMBR */
uint32_t counter_get_global; /* counter for GETNBGL */
uint32_t counter_try; /* counter for `try` statement */ uint32_t counter_try; /* counter for `try` statement */
uint32_t counter_exc; /* counter for raised exceptions */ uint32_t counter_exc; /* counter for raised exceptions */
uint32_t counter_gc_kept; /* counter for objects scanned by last gc */ uint32_t counter_gc_kept; /* counter for objects scanned by last gc */
uint32_t counter_gc_freed; /* counter for objects freed by last gc */ uint32_t counter_gc_freed; /* counter for objects freed by last gc */
uint32_t counter_mem_alloc; /* counter for memory allocations */
uint32_t counter_mem_free; /* counter for memory frees */
uint32_t counter_mem_realloc; /* counter for memory reallocations */
uint32_t micros_gc0; uint32_t micros_gc0;
uint32_t micros_gc1; uint32_t micros_gc1;

View File

@ -478,15 +478,18 @@ typedef bclass_ptr bclass_array[];
#endif #endif
/** /**
* @def PROTO_RUNTIME_BLOCK * @def BE_DEBUG_SOURCE_FILE
* @brief conditional block in bproto depending on compilation options * @brief conditional block in bproto depending on compilation options
* *
*/ */
#if BE_SOURCE_FILE #if BE_DEBUG_SOURCE_FILE
#define PROTO_SOURCE_FILE(n) \ #define PROTO_SOURCE_FILE(n) \
((bstring*) _source), /**< source */ ((bstring*) n), /**< source */
#define PROTO_SOURCE_FILE_STR(n) \
be_local_const_str(n##_str_source), /**< source */
#else #else
#define PROTO_SOURCE_FILE(n) #define PROTO_SOURCE_FILE(n)
#define PROTO_SOURCE_FILE_STR(n)
#endif #endif
/** /**
@ -496,8 +499,8 @@ typedef bclass_ptr bclass_array[];
*/ */
#if BE_DEBUG_RUNTIME_INFO #if BE_DEBUG_RUNTIME_INFO
#define PROTO_RUNTIME_BLOCK \ #define PROTO_RUNTIME_BLOCK \
NULL, /**< varinfo */ \ NULL, /**< lineinfo */ \
0, /**< nvarinfo */ 0, /**< nlineinfo */
#else #else
#define PROTO_RUNTIME_BLOCK #define PROTO_RUNTIME_BLOCK
#endif #endif
@ -538,7 +541,7 @@ typedef bclass_ptr bclass_array[];
BE_IIF(_is_subproto)((struct bproto**)&_name##_subproto,NULL), /**< bproto **ptab */ \ BE_IIF(_is_subproto)((struct bproto**)&_name##_subproto,NULL), /**< bproto **ptab */ \
(binstruction*) &_name##_code, /**< code */ \ (binstruction*) &_name##_code, /**< code */ \
be_local_const_str(_name##_str_name), /**< name */ \ be_local_const_str(_name##_str_name), /**< name */ \
be_local_const_str(_name##_str_source), /**< source */ \ PROTO_SOURCE_FILE_STR(_name) /**< source */ \
PROTO_RUNTIME_BLOCK /**< */ \ PROTO_RUNTIME_BLOCK /**< */ \
PROTO_VAR_INFO_BLOCK /**< */ \ PROTO_VAR_INFO_BLOCK /**< */ \
} }
@ -2062,7 +2065,9 @@ BERRY_API void be_exit(bvm *vm, int status);
* @param except * @param except
* @param msg * @param msg
*/ */
#ifdef __GNUC__
__attribute__((noreturn)) __attribute__((noreturn))
#endif
BERRY_API void be_raise(bvm *vm, const char *except, const char *msg); BERRY_API void be_raise(bvm *vm, const char *except, const char *msg);
/** /**

View File

@ -36,7 +36,13 @@ assert(bool(3.5) == true)
assert(bool('') == false) # changed behavior assert(bool('') == false) # changed behavior
assert(bool('a') == true) assert(bool('a') == true)
assert(bool(list) == true) assert(bool(list) == true)
assert(bool(list()) == true) assert(bool(list()) == false) # changed behavior
assert(bool([]) == false) # changed behavior
assert(bool([0]) == true)
assert(bool(map()) == false) # changed behavior
assert(bool({}) == false) # changed behavior
assert(bool({false:false}) == true)
assert(bool({nil:nil}) == false)# changed behavior - `nil` key is ignored so the map is empty
import introspect import introspect
assert(bool(introspect.toptr(0x1000)) == true) assert(bool(introspect.toptr(0x1000)) == true)

View File

@ -140,4 +140,18 @@ assert(classname(a) == 'A')
assert(classname(b) == 'B') assert(classname(b) == 'B')
assert(A.B.f() == 1) assert(A.B.f() == 1)
assert(b.g() == 2) assert(b.g() == 2)
assert(super(B) == nil) assert(super(A.B) == nil)
#- `_class` initializer can now be used in initializer code -#
class A
static var a = 1
static var b = _class
static var c = [_class.a, _class.b]
static def f(x)
return _class
end
end
assert(A.a == 1)
assert(A.b == A)
assert(A.c == [1, A])
assert(A.f(1) == A)

View File

@ -0,0 +1,39 @@
# test for ranges
# expand a range object as list
def expand(iter)
var ret = []
for i: iter
ret.push(i)
end
return ret
end
assert(expand(0..5) == [0, 1, 2, 3, 4, 5])
assert(expand(0..0) == [0])
assert(expand(5..0) == [])
var r = 1..5
assert(r.lower() == 1)
assert(r.upper() == 5)
assert(r.incr() == 1)
assert(expand(range(0,5)) == [0, 1, 2, 3, 4, 5])
assert(expand(range(0,5,2)) == [0, 2, 4])
assert(expand(range(0,5,12)) == [0])
assert(expand(range(0,5,-1)) == [])
assert(expand(range(5,0,-1)) == [5, 4, 3, 2, 1, 0])
assert(expand(range(5,0,-2)) == [5, 3, 1])
assert(expand(range(5,5,-2)) == [5])
assert(expand(range(0,5,-2)) == [])
def assert_value_error(c)
try
compile(c)()
assert(false, 'unexpected execution flow')
except 'value_error' as e, m
end
end
# range with increment zero shoud raise an error
assert_value_error("range(1,2,0)")

View File

@ -147,3 +147,25 @@ assert(string.format("%s", nil) == 'nil')
assert(string.format("%s", true) == 'true') assert(string.format("%s", true) == 'true')
assert(string.format("%s", false) == 'false') assert(string.format("%s", false) == 'false')
# format is now synonym to string.format
assert(format == string.format)
assert(format("%.1f", 3) == '3.0')
# f-strings
assert(f"" == '')
assert(f'' == '')
assert(f"abc\n\r\t" == 'abc\n\r\t')
assert(f'{{a}}' == '{a}')
assert(f'\\\\' == '\\\\')
assert(f"A = {1+1}" == 'A = 2')
assert(f"A = {1+1:s}" == 'A = 2')
assert(f"A = {1+1:i}" == 'A = 2')
assert(f"A = {1+1:04i}" == 'A = 0002')
assert(f"P = {3.1415:.2f}" == 'P = 3.14')
var a = 'foobar{0}'
assert(f"S = {a}" == 'S = foobar{0}')
assert(f"S = {a:i}" == 'S = 0')
assert(f"{a=}" == 'a=foobar{0}')

View File

@ -0,0 +1,35 @@
# test for the walrus operator
var a = 1
assert((a := a + 1) == 2)
assert((a := a + 1) == 3)
def f()
var defer = 10
var ret = []
for i:0..100
if (defer := defer - 1) == 0
ret.push(i)
defer = 10
end
end
return ret
end
assert(f() == [9, 19, 29, 39, 49, 59, 69, 79, 89, 99])
# test for expressions with no side effects
def assert_attribute_error(c)
try
compile(c)()
assert(false, 'unexpected execution flow')
except 'syntax_error' as e, m
end
end
# below the expressions `a` or `a b` have no side effect and do not generate any code, this is an error when in strict mode
import strict
var a, b
assert_attribute_error("var a,b def f() a b end")
assert_attribute_error("var a,b def f() a end")
# while the following does have side effect
def f() a := b end

View File

@ -5,9 +5,10 @@ block = {statement};
(* statement define *) (* statement define *)
statement = class_stmt | func_stmt | var_stmt | if_stmt | while_stmt | statement = class_stmt | func_stmt | var_stmt | if_stmt | while_stmt |
for_stmt | break_stmt | return_stmt | expr_stmt | import_stmt | for_stmt | break_stmt | return_stmt | expr_stmt | import_stmt |
try_stmt | throw_stmt | ';'; try_stmt | throw_stmt | do_stmt | ';';
if_stmt = 'if' expr block {'elif' expr block} ['else' block] 'end'; if_stmt = 'if' expr block {'elif' expr block} ['else' block] 'end';
while_stmt = 'while' expr block 'end'; while_stmt = 'while' expr block 'end';
do_stmt = 'do' block 'end';
for_stmt = 'for' ID ':' expr block 'end'; for_stmt = 'for' ID ':' expr block 'end';
break_stmt = 'break' | 'continue'; break_stmt = 'break' | 'continue';
return_stmt = 'return' [expr]; return_stmt = 'return' [expr];
@ -28,7 +29,7 @@ throw_stmt = 'raise' expr [',' expr];
var_stmt = 'var' ID ['=' expr] {',' ID ['=' expr]}; var_stmt = 'var' ID ['=' expr] {',' ID ['=' expr]};
(* expression define *) (* expression define *)
expr_stmt = expr [assign_op expr]; expr_stmt = expr [assign_op expr];
expr = suffix_expr | unop expr | expr binop expr | range_expr | cond_expr; expr = suffix_expr | unop expr | expr binop expr | range_expr | cond_expr | walrus_expr;
cond_expr = expr '?' expr ':' expr; (* conditional expression *) cond_expr = expr '?' expr ':' expr; (* conditional expression *)
assign_op = '=' | '+=' | '-=' | '*=' | '/=' | assign_op = '=' | '+=' | '-=' | '*=' | '/=' |
'%=' | '&=' | '|=' | '^=' | '<<=' | '>>='; '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=';
@ -36,9 +37,11 @@ binop = '<' | '<=' | '==' | '!=' | '>' | '>=' | '||' | '&&' |
'<<' | '>>' | '&' | '|' | '^' | '+' | '-' | '*' | '/' | '%'; '<<' | '>>' | '&' | '|' | '^' | '+' | '-' | '*' | '/' | '%';
range_expr = expr '..' [expr] range_expr = expr '..' [expr]
unop = '-' | '!' | '~'; unop = '-' | '!' | '~';
walrus_expr = expr ':=' expr
suffix_expr = primary_expr {call_expr | ('.' ID) | '[' expr ']'}; suffix_expr = primary_expr {call_expr | ('.' ID) | '[' expr ']'};
primary_expr = '(' expr ')' | simple_expr | list_expr | map_expr | anon_func | lambda_expr; primary_expr = '(' expr ')' | simple_expr | list_expr | map_expr | anon_func | lambda_expr;
simple_expr = INTEGER | REAL | STRING | ID | 'true' | 'false' | 'nil'; simple_expr = INTEGER | REAL | STRING | ID | 'true' | 'false' | 'nil' | f_string;
f_string = 'f' STRING
call_expr = '(' [expr {',' expr}] ')'; call_expr = '(' [expr {',' expr}] ')';
list_expr = '[' {expr ','} [expr] ']'; list_expr = '[' {expr ','} [expr] ']';
map_expr = '{' {expr ':' expr ','} [expr ':' expr] '}'; map_expr = '{' {expr ':' expr ','} [expr ':' expr] '}';

View File

@ -5,3 +5,4 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
## [Unreleased] ## [Unreleased]
- Initial release - Initial release
- add support for f-strings, `_class` and indent on `do`

View File

@ -26,7 +26,7 @@
["'", "'"] ["'", "'"]
], ],
"indentationRules": { "indentationRules": {
"increaseIndentPattern": "(^((\\s*(class|while|for|if|elif|else|try|except))|(.*\\bdef))\\b((?!\\b(end)\\b).)*)$", "increaseIndentPattern": "(^((\\s*(class|while|for|if|elif|else|try|except|do))|(.*\\bdef))\\b((?!\\b(end)\\b).)*)$",
"decreaseIndentPattern": "^\\s*((\\b(elif|else|except|end)\\b)|(\\)))" "decreaseIndentPattern": "^\\s*((\\b(elif|else|except|end)\\b)|(\\)))"
} }
} }

View File

@ -2,7 +2,7 @@
"name": "berry", "name": "berry",
"displayName": "Berry Script Language", "displayName": "Berry Script Language",
"description": "A small embedded script language.", "description": "A small embedded script language.",
"version": "0.1.0", "version": "1.1.0",
"publisher": "skiars", "publisher": "skiars",
"engines": { "engines": {
"vscode": "^1.15.1" "vscode": "^1.15.1"

View File

@ -44,11 +44,55 @@
"patterns": [ "patterns": [
{ {
"name": "string.quoted.double.berry", "name": "string.quoted.double.berry",
"match": "\"(\\\\.|[^\"])*\"" "begin": "(\"|')",
"end": "\\1",
"patterns": [
{
"name": "constant.character.escape.berry",
"match": "(\\\\x[\\h]{2})|(\\\\[0-7]{3})|(\\\\\\\\)|(\\\\\")|(\\\\')|(\\\\a)|(\\\\b)|(\\\\f)|(\\\\n)|(\\\\r)|(\\\\t)|(\\\\v)"
}
]
}, },
{ {
"name": "string.quoted.single.berry", "name": "string.quoted.other.berry",
"match": "'(\\\\.|[^'])*'" "begin": "f(\"|')",
"end": "\\1",
"patterns": [
{
"name": "constant.character.escape.berry",
"match": "(\\\\x[\\h]{2})|(\\\\[0-7]{3})|(\\\\\\\\)|(\\\\\")|(\\\\')|(\\\\a)|(\\\\b)|(\\\\f)|(\\\\n)|(\\\\r)|(\\\\t)|(\\\\v)"
},
{
"name": "string.quoted.other.berry",
"match": "\\{\\{[^\\}]*\\}\\}"
},
{
"name": "keyword.other.unit.berry",
"begin": "\\{",
"end": "\\}",
"patterns": [
{
"include": "#keywords"
},
{
"include": "#numbers"
},
{
"include": "#identifier"
},
{
"include": "#operator"
},
{
"include": "#member"
},
{
"include": "#function"
}
]
}
]
} }
] ]
}, },
@ -67,7 +111,7 @@
"keywords": { "keywords": {
"patterns": [{ "patterns": [{
"name": "keyword.berry", "name": "keyword.berry",
"match": "\\b(var|static|def|class|true|false|nil|self|super|import|as)\\b" "match": "\\b(var|static|def|class|true|false|nil|self|super|import|as|_class)\\b"
}] }]
}, },
"identifier": { "identifier": {

View File

@ -1,5 +1,5 @@
{ {
"name": "Berry int64 implementation for 32 bits architceture", "name": "Berry int64 implementation for 32 bits architecture",
"version": "1.0", "version": "1.0",
"description": "Berry int64", "description": "Berry int64",
"license": "MIT", "license": "MIT",

View File

@ -22,10 +22,10 @@ enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_D
* We allow 4 parameters, or 3 if method (first arg is `self`) * We allow 4 parameters, or 3 if method (first arg is `self`)
* This could be extended if needed * This could be extended if needed
\*********************************************************************************************/ \*********************************************************************************************/
typedef int32_t (*berry_callback_t)(int32_t v0, int32_t v1, int32_t v2, int32_t v3); typedef int (*berry_callback_t)(int v0, int v1, int v2, int v3);
static int32_t call_berry_cb(int32_t num, int32_t v0, int32_t v1, int32_t v2, int32_t v3); static int call_berry_cb(int num, int v0, int v1, int v2, int v3);
#define BERRY_CB(n) int32_t berry_cb_##n(int32_t v0, int32_t v1, int32_t v2, int32_t v3) { return call_berry_cb(n, v0, v1, v2, v3); } #define BERRY_CB(n) int berry_cb_##n(int v0, int v1, int v2, int v3) { return call_berry_cb(n, v0, v1, v2, v3); }
// list the callbacks // list the callbacks
BERRY_CB(0); BERRY_CB(0);
BERRY_CB(1); BERRY_CB(1);
@ -85,7 +85,7 @@ typedef struct be_callback_handler_list_t {
static be_callback_hook be_cb_hooks[BE_MAX_CB] = {0}; static be_callback_hook be_cb_hooks[BE_MAX_CB] = {0};
static int32_t be_cb_gen_cb(bvm *vm); static int be_cb_gen_cb(bvm *vm);
static be_callback_handler_list_t be_callback_default_gen_cb = { static be_callback_handler_list_t be_callback_default_gen_cb = {
NULL, NULL,
be_const_func(&be_cb_gen_cb), be_const_func(&be_cb_gen_cb),
@ -102,7 +102,7 @@ static be_callback_handler_list_t *be_callback_handler_list_head = &be_callback_
* *
* arg1: function (or closure) * arg1: function (or closure)
\*********************************************************************************************/ \*********************************************************************************************/
static int32_t be_cb_add_handler(bvm *vm) { static int be_cb_add_handler(bvm *vm) {
int32_t top = be_top(vm); int32_t top = be_top(vm);
if (top >= 1 && be_isfunction(vm, 1)) { if (top >= 1 && be_isfunction(vm, 1)) {
bvalue *v = be_indexof(vm, 1); bvalue *v = be_indexof(vm, 1);
@ -129,7 +129,7 @@ static int32_t be_cb_add_handler(bvm *vm) {
* *
* No args * No args
\*********************************************************************************************/ \*********************************************************************************************/
static int32_t be_cb_list_handlers(bvm *vm) { static int be_cb_list_handlers(bvm *vm) {
be_newobject(vm, "list"); be_newobject(vm, "list");
for (be_callback_handler_list_t *elt = be_callback_handler_list_head; elt != NULL; elt = elt->next) { for (be_callback_handler_list_t *elt = be_callback_handler_list_head; elt != NULL; elt = elt->next) {
if (elt->vm == vm) { /* on purpose don't show the default handler, just pretend it's not there since it's default */ if (elt->vm == vm) { /* on purpose don't show the default handler, just pretend it's not there since it's default */
@ -153,7 +153,7 @@ static int32_t be_cb_list_handlers(bvm *vm) {
* arg2: type name for callback (optional) * arg2: type name for callback (optional)
* argN: any other callback specific arguments (unlimited number, passed as-is) * argN: any other callback specific arguments (unlimited number, passed as-is)
\*********************************************************************************************/ \*********************************************************************************************/
static int32_t be_cb_make_cb(bvm *vm) { static int be_cb_make_cb(bvm *vm) {
int32_t argc = be_top(vm); int32_t argc = be_top(vm);
if (argc >= 1 && be_isfunction(vm, 1)) { if (argc >= 1 && be_isfunction(vm, 1)) {
@ -187,7 +187,7 @@ static int32_t be_cb_make_cb(bvm *vm) {
* *
* arg1: function (or closure) * arg1: function (or closure)
\*********************************************************************************************/ \*********************************************************************************************/
static int32_t be_cb_gen_cb(bvm *vm) { static int be_cb_gen_cb(bvm *vm) {
int32_t top = be_top(vm); int32_t top = be_top(vm);
// tasmota_log_C(LOG_LEVEL_DEBUG, "BRY: gen_cb() called"); // tasmota_log_C(LOG_LEVEL_DEBUG, "BRY: gen_cb() called");
if (top >= 1 && be_isfunction(vm, 1)) { if (top >= 1 && be_isfunction(vm, 1)) {
@ -217,7 +217,7 @@ static int32_t be_cb_gen_cb(bvm *vm) {
* `get_cb_list`: Return the list of callbacks for this vm * `get_cb_list`: Return the list of callbacks for this vm
* *
\*********************************************************************************************/ \*********************************************************************************************/
static int32_t be_cb_get_cb_list(bvm *vm) { static int be_cb_get_cb_list(bvm *vm) {
be_newobject(vm, "list"); be_newobject(vm, "list");
for (uint32_t i=0; i < BE_MAX_CB; i++) { for (uint32_t i=0; i < BE_MAX_CB; i++) {
if (be_cb_hooks[i].vm) { if (be_cb_hooks[i].vm) {
@ -242,7 +242,7 @@ static int32_t be_cb_get_cb_list(bvm *vm) {
* We allow 4 parameters, or 3 if method (first arg is `self`) * We allow 4 parameters, or 3 if method (first arg is `self`)
* This could be extended if needed * This could be extended if needed
\*********************************************************************************************/ \*********************************************************************************************/
static int32_t call_berry_cb(int32_t num, int32_t v0, int32_t v1, int32_t v2, int32_t v3) { static int call_berry_cb(int num, int v0, int v1, int v2, int v3) {
// call berry cb dispatcher // call berry cb dispatcher
int32_t ret = 0; int32_t ret = 0;
// retrieve vm and function // retrieve vm and function
@ -271,6 +271,26 @@ static int32_t call_berry_cb(int32_t num, int32_t v0, int32_t v1, int32_t v2, in
return ret; return ret;
} }
/*********************************************************************************************\
* `be_cb_deinit`:
* Clean any callback for this VM, they shouldn't call the registerd function anymore
\*********************************************************************************************/
void be_cb_deinit(bvm *vm) {
// remove all cb for this vm
for (int32_t slot = 0; slot < BE_MAX_CB; slot++) {
if (be_cb_hooks[slot].vm == vm) {
be_cb_hooks[slot].vm = NULL;
be_cb_hooks[slot].f.type == BE_NIL;
}
}
// remove the vm gen_cb for this vm
for (be_callback_handler_list_t **elt_ptr = &be_callback_handler_list_head; *elt_ptr != NULL; elt_ptr = &(*elt_ptr)->next) {
if (((*elt_ptr)->next != NULL) && ((*elt_ptr)->next->vm == vm)) {
(*elt_ptr)->next = (*elt_ptr)->next->next;
}
}
}
/* @const_object_info_begin /* @const_object_info_begin
module cb (scope: global) { module cb (scope: global) {
gen_cb, func(be_cb_gen_cb) gen_cb, func(be_cb_gen_cb)

View File

@ -109,6 +109,8 @@ extern int be_check_arg_type(bvm *vm, int arg_start, int argc, const char * arg_
extern int be_call_c_func(bvm *vm, const void * func, const char * return_type, const char * arg_type); extern int be_call_c_func(bvm *vm, const void * func, const char * return_type, const char * arg_type);
extern int be_call_ctype_func(bvm *vm, const void *definition); /* handler for Berry vm */ extern int be_call_ctype_func(bvm *vm, const void *definition); /* handler for Berry vm */
extern void be_cb_deinit(bvm *vm); /* remove all callbacks from the VM (just before shutdown of VM) */
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -31,6 +31,7 @@ static uint8_t ip_bytes[16] = {};
extern "C" const void* matter_get_ip_bytes(const char* ip_str, size_t* ret_len) { extern "C" const void* matter_get_ip_bytes(const char* ip_str, size_t* ret_len) {
IPAddress ip; IPAddress ip;
if (ip.fromString(ip_str)) { if (ip.fromString(ip_str)) {
#ifdef USE_IPV6
if (ip.isV4()) { if (ip.isV4()) {
uint32_t ip_32 = ip; uint32_t ip_32 = ip;
memcpy(ip_bytes, &ip_32, 4); memcpy(ip_bytes, &ip_32, 4);
@ -39,6 +40,11 @@ extern "C" const void* matter_get_ip_bytes(const char* ip_str, size_t* ret_len)
memcpy(ip_bytes, ip.raw6(), 16); memcpy(ip_bytes, ip.raw6(), 16);
*ret_len = 16; *ret_len = 16;
} }
#else
uint32_t ip_32 = ip;
memcpy(ip_bytes, &ip_32, 4);
*ret_len = 4;
#endif
return ip_bytes; return ip_bytes;
} else { } else {
*ret_len = 0; *ret_len = 0;

View File

@ -204,6 +204,7 @@ extern const bclass be_class_Matter_TLV; // need to declare it upfront because
#include "solidify/solidified_Matter_Base38.h" #include "solidify/solidified_Matter_Base38.h"
#include "solidify/solidified_Matter_UI.h" #include "solidify/solidified_Matter_UI.h"
#include "solidify/solidified_Matter_Device.h" #include "solidify/solidified_Matter_Device.h"
#include "solidify/solidified_Matter_Profiler.h"
#include "../generate/be_matter_certs.h" #include "../generate/be_matter_certs.h"
@ -287,6 +288,7 @@ module matter (scope: global, strings: weak) {
sort, closure(matter_sort_closure) sort, closure(matter_sort_closure)
jitter, closure(matter_jitter_closure) jitter, closure(matter_jitter_closure)
inspect, closure(matter_inspect_closure) inspect, closure(matter_inspect_closure)
Profiler, class(be_class_Matter_Profiler)
// Status codes // Status codes
SUCCESS, int(0x00) SUCCESS, int(0x00)
@ -336,6 +338,7 @@ module matter (scope: global, strings: weak) {
StatusIB, class(be_class_Matter_StatusIB) StatusIB, class(be_class_Matter_StatusIB)
StatusResponseMessage, class(be_class_Matter_StatusResponseMessage) StatusResponseMessage, class(be_class_Matter_StatusResponseMessage)
ReadRequestMessage, class(be_class_Matter_ReadRequestMessage) ReadRequestMessage, class(be_class_Matter_ReadRequestMessage)
ReadRequestMessage_solo, class(be_class_Matter_ReadRequestMessage_solo)
ReportDataMessage, class(be_class_Matter_ReportDataMessage) ReportDataMessage, class(be_class_Matter_ReportDataMessage)
SubscribeRequestMessage, class(be_class_Matter_SubscribeRequestMessage) SubscribeRequestMessage, class(be_class_Matter_SubscribeRequestMessage)
SubscribeResponseMessage, class(be_class_Matter_SubscribeResponseMessage) SubscribeResponseMessage, class(be_class_Matter_SubscribeResponseMessage)
@ -343,6 +346,7 @@ module matter (scope: global, strings: weak) {
WriteResponseMessage, class(be_class_Matter_WriteResponseMessage) WriteResponseMessage, class(be_class_Matter_WriteResponseMessage)
TimedRequestMessage, class(be_class_Matter_TimedRequestMessage) TimedRequestMessage, class(be_class_Matter_TimedRequestMessage)
InvokeRequestMessage, class(be_class_Matter_InvokeRequestMessage) InvokeRequestMessage, class(be_class_Matter_InvokeRequestMessage)
InvokeRequestMessage_solo, class(be_class_Matter_InvokeRequestMessage_solo)
InvokeResponseMessage, class(be_class_Matter_InvokeResponseMessage) InvokeResponseMessage, class(be_class_Matter_InvokeResponseMessage)
// Matter Commisioning messages // Matter Commisioning messages

View File

@ -32,7 +32,7 @@
// `matter.QRCode.encode_str(content:string) -> map` // `matter.QRCode.encode_str(content:string) -> map`
// //
int32_t qr_encode_str(bvm *vm) { int qr_encode_str(bvm *vm) {
int32_t argc = be_top(vm); int32_t argc = be_top(vm);
if (argc >= 1 && be_isstring(vm, 1)) { if (argc >= 1 && be_isstring(vm, 1)) {
const char * data_str = be_tostring(vm, 1); const char * data_str = be_tostring(vm, 1);

View File

@ -130,7 +130,7 @@ class Matter_Commisioning_Context
# record the initiator_session_id # record the initiator_session_id
session.__future_initiator_session_id = pbkdfparamreq.initiator_session_id session.__future_initiator_session_id = pbkdfparamreq.initiator_session_id
session.__future_local_session_id = self.device.sessions.gen_local_session_id() session.__future_local_session_id = self.device.sessions.gen_local_session_id()
tasmota.log(format("MTR: New_Session(%6i) from '[%s]:%i'", session.__future_local_session_id, msg.remote_ip, msg.remote_port), 3) tasmota.log(format("MTR: +Session (%6i) from '[%s]:%i'", session.__future_local_session_id, msg.remote_ip, msg.remote_port), 3)
# prepare response # prepare response
var pbkdfparamresp = matter.PBKDFParamResponse() var pbkdfparamresp = matter.PBKDFParamResponse()
@ -235,7 +235,7 @@ class Matter_Commisioning_Context
var raw = resp.encode_frame(pake2_raw) var raw = resp.encode_frame(pake2_raw)
# log the fact that a new commissioning is starting # log the fact that a new commissioning is starting
tasmota.log(format("MTR: New Commissioning (PASE id=%i) from [%s]:%i", session.__future_local_session_id, session._ip, session._port)) tasmota.log(format("MTR: New Commissioning (PASE id=%i) from [%s]:%i", session.__future_local_session_id, session._ip, session._port), 2)
self.responder.send_response_frame(resp) self.responder.send_response_frame(resp)
return true return true
@ -265,7 +265,7 @@ class Matter_Commisioning_Context
end end
# send PakeFinished and compute session key # send PakeFinished and compute session key
var created = tasmota.rtc()['utc'] var created = tasmota.rtc_utc()
var session_keys = crypto.HKDF_SHA256().derive(session.__spake_Ke, bytes(), bytes().fromstring(self.SEKeys_Info), 48) var session_keys = crypto.HKDF_SHA256().derive(session.__spake_Ke, bytes(), bytes().fromstring(self.SEKeys_Info), 48)
var I2RKey = session_keys[0..15] var I2RKey = session_keys[0..15]
var R2IKey = session_keys[16..31] var R2IKey = session_keys[16..31]
@ -289,18 +289,22 @@ class Matter_Commisioning_Context
import crypto import crypto
# Validate Sigma1 Destination ID, p.162 # Validate Sigma1 Destination ID, p.162
# traverse all existing fabrics # traverse all existing fabrics
tasmota.log("MTR: SEARCHING: destinationId=" + destinationId.tohex(), 4) if tasmota.loglevel(4)
tasmota.log("MTR: SEARCHING: destinationId=" + destinationId.tohex(), 4)
end
for fabric : self.device.sessions.fabrics for fabric : self.device.sessions.fabrics
if fabric.noc == nil || fabric.fabric_id == nil || fabric.device_id == nil continue end if fabric.noc == nil || fabric.fabric_id == nil || fabric.device_id == nil continue end
# compute candidateDestinationId, Section 4.13.2.4.1, “Destination Identifier” # compute candidateDestinationId, Section 4.13.2.4.1, “Destination Identifier”
var destinationMessage = initiatorRandom + fabric.get_ca_pub() + fabric.fabric_id + fabric.device_id var destinationMessage = initiatorRandom + fabric.get_ca_pub() + fabric.fabric_id + fabric.device_id
var key = fabric.get_ipk_group_key() var key = fabric.get_ipk_group_key()
tasmota.log("MTR: SIGMA1: destinationMessage=" + destinationMessage.tohex(), 4) # tasmota.log("MTR: SIGMA1: destinationMessage=" + destinationMessage.tohex(), 4)
# tasmota.log("MTR: SIGMA1: key_ipk=" + key.tohex(), 4) # tasmota.log("MTR: SIGMA1: key_ipk=" + key.tohex(), 4)
var h = crypto.HMAC_SHA256(key) var h = crypto.HMAC_SHA256(key)
h.update(destinationMessage) h.update(destinationMessage)
var candidateDestinationId = h.out() var candidateDestinationId = h.out()
tasmota.log("MTR: SIGMA1: candidateDestinationId=" + candidateDestinationId.tohex(), 4) if tasmota.loglevel(4)
tasmota.log("MTR: SIGMA1: candidateDestinationId=" + candidateDestinationId.tohex(), 4)
end
if candidateDestinationId == destinationId if candidateDestinationId == destinationId
return fabric return fabric
end end
@ -370,7 +374,7 @@ class Matter_Commisioning_Context
session.set_mode_CASE() session.set_mode_CASE()
session.__future_initiator_session_id = sigma1.initiator_session_id # update initiator_session_id session.__future_initiator_session_id = sigma1.initiator_session_id # update initiator_session_id
session.__future_local_session_id = self.device.sessions.gen_local_session_id() session.__future_local_session_id = self.device.sessions.gen_local_session_id()
tasmota.log(format("MTR: New_Session(%6i) from '[%s]:%i'", session.__future_local_session_id, msg.remote_ip, msg.remote_port), 3) tasmota.log(format("MTR: +Session (%6i) from '[%s]:%i'", session.__future_local_session_id, msg.remote_ip, msg.remote_port), 3)
# Generate and Send Sigma2_Resume # Generate and Send Sigma2_Resume
session.shared_secret = session_resumption.shared_secret session.shared_secret = session_resumption.shared_secret
@ -406,7 +410,7 @@ class Matter_Commisioning_Context
var i2r = session_keys[0..15] var i2r = session_keys[0..15]
var r2i = session_keys[16..31] var r2i = session_keys[16..31]
var ac = session_keys[32..47] var ac = session_keys[32..47]
var created = tasmota.rtc()['utc'] var created = tasmota.rtc_utc()
# tasmota.log("MTR: ******************************", 4) # tasmota.log("MTR: ******************************", 4)
# tasmota.log("MTR: I2RKey =" + i2r.tohex(), 4) # tasmota.log("MTR: I2RKey =" + i2r.tohex(), 4)
@ -458,7 +462,7 @@ class Matter_Commisioning_Context
session.__future_initiator_session_id = sigma1.initiator_session_id # update initiator_session_id session.__future_initiator_session_id = sigma1.initiator_session_id # update initiator_session_id
session.__future_local_session_id = self.device.sessions.gen_local_session_id() session.__future_local_session_id = self.device.sessions.gen_local_session_id()
tasmota.log(format("MTR: New_Session(%6i) from '[%s]:%i'", session.__future_local_session_id, msg.remote_ip, msg.remote_port), 3) tasmota.log(format("MTR: +Session (%6i) from '[%s]:%i'", session.__future_local_session_id, msg.remote_ip, msg.remote_port), 3)
# tasmota.log("MTR: fabric="+matter.inspect(session._fabric), 4) # tasmota.log("MTR: fabric="+matter.inspect(session._fabric), 4)
# tasmota.log("MTR: no_private_key="+session.get_pk().tohex(), 4) # tasmota.log("MTR: no_private_key="+session.get_pk().tohex(), 4)
@ -538,7 +542,7 @@ class Matter_Commisioning_Context
var raw = resp.encode_frame(sigma2_raw) var raw = resp.encode_frame(sigma2_raw)
# log the fact that a new connection is starting # log the fact that a new connection is starting
tasmota.log(format("MTR: New Connection (CASE id=%i) from [%s]:%i", session.__future_local_session_id, session._ip, session._port)) tasmota.log(format("MTR: New Connection (CASE id=%i) from [%s]:%i", session.__future_local_session_id, session._ip, session._port), 2)
self.responder.send_response_frame(resp) self.responder.send_response_frame(resp)
return true return true
@ -658,7 +662,7 @@ class Matter_Commisioning_Context
var i2r = session_keys[0..15] var i2r = session_keys[0..15]
var r2i = session_keys[16..31] var r2i = session_keys[16..31]
var ac = session_keys[32..47] var ac = session_keys[32..47]
var created = tasmota.rtc()['utc'] var created = tasmota.rtc_utc()
# tasmota.log("MTR: ******************************", 4) # tasmota.log("MTR: ******************************", 4)
# tasmota.log("MTR: I2RKey =" + i2r.tohex(), 4) # tasmota.log("MTR: I2RKey =" + i2r.tohex(), 4)

View File

@ -35,6 +35,7 @@ class Matter_Device
var plugins_config # map of JSON configuration for plugins var plugins_config # map of JSON configuration for plugins
var plugins_config_remotes # map of information on each remote under "remotes" key, '{}' when empty var plugins_config_remotes # map of information on each remote under "remotes" key, '{}' when empty
var udp_server # `matter.UDPServer()` object var udp_server # `matter.UDPServer()` object
var profiler
var message_handler # `matter.MessageHandler()` object var message_handler # `matter.MessageHandler()` object
var sessions # `matter.Session_Store()` objet var sessions # `matter.Session_Store()` objet
var ui var ui
@ -63,6 +64,7 @@ class Matter_Device
var root_discriminator # as `int` var root_discriminator # as `int`
var root_passcode # as `int` var root_passcode # as `int`
var ipv4only # advertize only IPv4 addresses (no IPv6) var ipv4only # advertize only IPv4 addresses (no IPv6)
var disable_bridge_mode # default is bridge mode, this flag disables this mode for some non-compliant controllers
var next_ep # next endpoint to be allocated for bridge, start at 1 var next_ep # next endpoint to be allocated for bridge, start at 1
# context for PBKDF # context for PBKDF
var root_iterations # PBKDF number of iterations var root_iterations # PBKDF number of iterations
@ -79,6 +81,7 @@ class Matter_Device
return return
end # abort if SetOption 151 is not set end # abort if SetOption 151 is not set
matter.profiler = matter.Profiler()
self.started = false self.started = false
self.tick = 0 self.tick = 0
self.plugins = [] self.plugins = []
@ -92,6 +95,7 @@ class Matter_Device
self.next_ep = 1 # start at endpoint 1 for dynamically allocated endpoints self.next_ep = 1 # start at endpoint 1 for dynamically allocated endpoints
self.root_salt = crypto.random(16) self.root_salt = crypto.random(16)
self.ipv4only = false self.ipv4only = false
self.disable_bridge_mode = false
self.load_param() self.load_param()
self.sessions = matter.Session_Store(self) self.sessions = matter.Session_Store(self)
@ -172,18 +176,24 @@ class Matter_Device
##################################################################### #####################################################################
# Remove a fabric and clean all corresponding values and mDNS entries # Remove a fabric and clean all corresponding values and mDNS entries
def remove_fabric(fabric_parent) def remove_fabric(fabric)
var sub_fabrics = self.sessions.find_children_fabrics(fabric_parent.get_fabric_index()) if fabric != nil
if sub_fabrics == nil return end tasmota.log("MTR: removing fabric " + fabric.get_fabric_id().copy().reverse().tohex(), 2)
for fabric_index : sub_fabrics self.message_handler.im.subs_shop.remove_by_fabric(fabric)
var fabric = self.sessions.find_fabric_by_index(fabric_index) self.mdns_remove_op_discovery(fabric)
if fabric != nil self.sessions.remove_fabric(fabric)
tasmota.log("MTR: removing fabric " + fabric.get_fabric_id().copy().reverse().tohex(), 2)
self.message_handler.im.subs_shop.remove_by_fabric(fabric)
self.mdns_remove_op_discovery(fabric)
self.sessions.remove_fabric(fabric)
end
end end
# var sub_fabrics = self.sessions.find_children_fabrics(fabric_parent.get_fabric_index())
# if sub_fabrics == nil return end
# for fabric_index : sub_fabrics
# var fabric = self.sessions.find_fabric_by_index(fabric_index)
# if fabric != nil
# tasmota.log("MTR: removing fabric " + fabric.get_fabric_id().copy().reverse().tohex(), 2)
# self.message_handler.im.subs_shop.remove_by_fabric(fabric)
# self.mdns_remove_op_discovery(fabric)
# self.sessions.remove_fabric(fabric)
# end
# end
self.sessions.save_fabrics() self.sessions.save_fabrics()
end end
@ -379,7 +389,7 @@ class Matter_Device
if self.udp_server return end # already started if self.udp_server return end # already started
if port == nil port = 5540 end if port == nil port = 5540 end
tasmota.log("MTR: Starting UDP server on port: " + str(port), 2) tasmota.log("MTR: Starting UDP server on port: " + str(port), 2)
self.udp_server = matter.UDPServer("", port) self.udp_server = matter.UDPServer(self, "", port)
self.udp_server.start(/ raw, addr, port -> self.msg_received(raw, addr, port)) self.udp_server.start(/ raw, addr, port -> self.msg_received(raw, addr, port))
end end
@ -427,7 +437,7 @@ class Matter_Device
var fabric = session.get_fabric() var fabric = session.get_fabric()
var fabric_id = fabric.get_fabric_id().copy().reverse().tohex() var fabric_id = fabric.get_fabric_id().copy().reverse().tohex()
var vendor_name = fabric.get_admin_vendor_name() var vendor_name = fabric.get_admin_vendor_name()
tasmota.log(format("MTR: --- Commissioning complete for Fabric '%s' (Vendor %s) ---", fabric_id, vendor_name), 2) tasmota.log(f"MTR: --- Commissioning complete for Fabric '{fabric_id}' (Vendor {vendor_name}) ---", 2)
self.stop_basic_commissioning() # by default close commissioning when it's complete self.stop_basic_commissioning() # by default close commissioning when it's complete
end end
@ -510,18 +520,15 @@ class Matter_Device
end end
var endpoint = ctx.endpoint var endpoint = ctx.endpoint
# var endpoint_mono = [ endpoint ]
var endpoint_found = false # did any endpoint match
var cluster = ctx.cluster var cluster = ctx.cluster
# var cluster_mono = [ cluster ]
var cluster_found = false
var attribute = ctx.attribute var attribute = ctx.attribute
# var attribute_mono = [ attribute ] var endpoint_found = false # did any endpoint match
var cluster_found = false
var attribute_found = false var attribute_found = false
var direct = (ctx.endpoint != nil) && (ctx.cluster != nil) && (ctx.attribute != nil) # true if the target is a precise attribute, false if it results from an expansion and error are ignored var direct = (ctx.endpoint != nil) && (ctx.cluster != nil) && (ctx.attribute != nil) # true if the target is a precise attribute, false if it results from an expansion and error are ignored
# tasmota.log(format("MTR: process_attribute_expansion %s", str(ctx)), 4) # tasmota.log(f"MTR: process_attribute_expansion {str(ctx))}", 4)
# build the list of candidates # build the list of candidates
@ -537,7 +544,7 @@ class Matter_Device
endpoint_found = true endpoint_found = true
# now explore the cluster list for 'ep' # now explore the cluster list for 'ep'
var cluster_list = pi.get_cluster_list(ep) # cluster_list is the actual list of candidate cluster for this pluging and endpoint var cluster_list = pi.get_cluster_list() # cluster_list is the actual list of candidate cluster for this pluging and endpoint
# tasmota.log(format("MTR: pi=%s ep=%s cl_list=%s", str(pi), str(ep), str(cluster_list)), 4) # tasmota.log(format("MTR: pi=%s ep=%s cl_list=%s", str(pi), str(ep), str(cluster_list)), 4)
for cl: cluster_list for cl: cluster_list
if cluster != nil && cl != cluster continue end # skip if specific cluster and no match if cluster != nil && cl != cluster continue end # skip if specific cluster and no match
@ -546,7 +553,7 @@ class Matter_Device
cluster_found = true cluster_found = true
# now filter on attributes # now filter on attributes
var attr_list = pi.get_attribute_list(ep, cl) var attr_list = pi.get_attribute_list(cl)
# tasmota.log(format("MTR: pi=%s ep=%s cl=%s at_list=%s", str(pi), str(ep), str(cl), str(attr_list)), 4) # tasmota.log(format("MTR: pi=%s ep=%s cl=%s at_list=%s", str(pi), str(ep), str(cl), str(attr_list)), 4)
for at: attr_list for at: attr_list
if attribute != nil && at != attribute continue end # skip if specific attribute and no match if attribute != nil && at != attribute continue end # skip if specific attribute and no match
@ -591,6 +598,44 @@ class Matter_Device
end end
end end
#############################################################
# Optimized version for a single endpoint/cluster/attribute
#
# Retrieve the plugin for a read
def process_attribute_read_solo(ctx)
var endpoint = ctx.endpoint
# var endpoint_found = false # did any endpoint match
var cluster = ctx.cluster
# var cluster_found = false
var attribute = ctx.attribute
# var attribute_found = false
# all 3 elements must be non-nil
if endpoint == nil || cluster == nil || attribute == nil return nil end
# look for plugin
var pi = self.find_plugin_by_endpoint(endpoint)
if pi == nil # endpoint not found
ctx.status = matter.UNSUPPORTED_ENDPOINT
return nil
end
# check cluster
if !pi.contains_cluster(cluster)
ctx.status = matter.UNSUPPORTED_CLUSTER
return nil
end
# attribute list
if !pi.contains_attribute(cluster, attribute)
ctx.status = matter.UNSUPPORTED_ATTRIBUTE
return nil
end
# all good
return pi
end
############################################################# #############################################################
# Return the list of endpoints from all plugins (distinct), exclud endpoint zero if `exclude_zero` is `true` # Return the list of endpoints from all plugins (distinct), exclud endpoint zero if `exclude_zero` is `true`
def get_active_endpoints(exclude_zero) def get_active_endpoints(exclude_zero)
@ -628,7 +673,7 @@ class Matter_Device
import json import json
self.update_remotes_info() # update self.plugins_config_remotes self.update_remotes_info() # update self.plugins_config_remotes
var j = format('{"distinguish":%i,"passcode":%i,"ipv4only":%s,"nextep":%i', self.root_discriminator, self.root_passcode, self.ipv4only ? 'true':'false', self.next_ep) var j = format('{"distinguish":%i,"passcode":%i,"ipv4only":%s,"disable_bridge_mode":%s,"nextep":%i', self.root_discriminator, self.root_passcode, self.ipv4only ? 'true':'false', self.disable_bridge_mode ? 'true':'false', self.next_ep)
if self.plugins_persist if self.plugins_persist
j += ',"config":' j += ',"config":'
j += json.dump(self.plugins_config) j += json.dump(self.plugins_config)
@ -642,7 +687,7 @@ class Matter_Device
var f = open(self.FILENAME, "w") var f = open(self.FILENAME, "w")
f.write(j) f.write(j)
f.close() f.close()
tasmota.log(format("MTR: =Saved parameters%s", self.plugins_persist ? " and configuration" : ""), 3) tasmota.log(format("MTR: =Saved parameters%s", self.plugins_persist ? " and configuration" : ""), 2)
return j return j
except .. as e, m except .. as e, m
tasmota.log("MTR: Session_Store::save Exception:" + str(e) + "|" + str(m), 2) tasmota.log("MTR: Session_Store::save Exception:" + str(e) + "|" + str(m), 2)
@ -693,6 +738,7 @@ class Matter_Device
self.root_discriminator = j.find("distinguish", self.root_discriminator) self.root_discriminator = j.find("distinguish", self.root_discriminator)
self.root_passcode = j.find("passcode", self.root_passcode) self.root_passcode = j.find("passcode", self.root_passcode)
self.ipv4only = bool(j.find("ipv4only", false)) self.ipv4only = bool(j.find("ipv4only", false))
self.disable_bridge_mode = bool(j.find("disable_bridge_mode", false))
self.next_ep = j.find("nextep", self.next_ep) self.next_ep = j.find("nextep", self.next_ep)
self.plugins_config = j.find("config") self.plugins_config = j.find("config")
if self.plugins_config != nil if self.plugins_config != nil
@ -1106,13 +1152,13 @@ class Matter_Device
if !r_st13.contains(k) break end # no more SHTxxx if !r_st13.contains(k) break end # no more SHTxxx
var d = r_st13[k] var d = r_st13[k]
tasmota.log(format("MTR: '%s' = %s", k, str(d)), 3) tasmota.log(format("MTR: '%s' = %s", k, str(d)), 3)
var relay1 = d.find('Relay1', 0) - 1 # relay base 0 or -1 if none var relay1 = d.find('Relay1', -1) # relay base 1 or -1 if none
var relay2 = d.find('Relay2', 0) - 1 # relay base 0 or -1 if none var relay2 = d.find('Relay2', -1) # relay base 1 or -1 if none
if relay1 >= 0 relays_reserved.push(relay1) end # mark relay1/2 as non-relays if relay1 > 0 relays_reserved.push(relay1 - 1) end # mark relay1/2 as non-relays
if relay2 >= 0 relays_reserved.push(relay2) end if relay2 > 0 relays_reserved.push(relay2 - 1) end
tasmota.log(format("MTR: relay1 = %s, relay2 = %s", relay1, relay2), 3) tasmota.log(f"MTR: {relay1=} {relay2=}", 3)
# is there tilt support # is there tilt support
var tilt_array = d.find('TiltConfig') var tilt_array = d.find('TiltConfig')
var tilt_config = tilt_array && (tilt_array[2] > 0) var tilt_config = tilt_array && (tilt_array[2] > 0)
@ -1131,7 +1177,7 @@ class Matter_Device
while relay_index < relay_count while relay_index < relay_count
if relays_reserved.find(relay_index) == nil # if relay is actual relay if relays_reserved.find(relay_index) == nil # if relay is actual relay
m[str(endpoint)] = {'type':'relay','relay':relay_index} m[str(endpoint)] = {'type':'relay','relay':relay_index + 1} # Relay index start with 1
endpoint += 1 endpoint += 1
end end
relay_index += 1 relay_index += 1
@ -1312,23 +1358,23 @@ class Matter_Device
self.plugins_config.remove(ep_str) self.plugins_config.remove(ep_str)
self.plugins_persist = true self.plugins_persist = true
# try saving parameters
self.save_param()
self.signal_endpoints_changed()
# now remove from in-memory configuration # now remove from in-memory configuration
var idx = 0 var idx = 0
while idx < size(self.plugins) while idx < size(self.plugins)
if ep == self.plugins[idx].get_endpoint() if ep == self.plugins[idx].get_endpoint()
self.plugins.remove(idx) self.plugins.remove(idx)
self.signal_endpoints_changed()
break break
else else
idx += 1 idx += 1
end end
end end
# clean any orphan remote # clean any orphan remote
self.clean_remotes() self.clean_remotes()
# try saving parameters
self.save_param()
self.signal_endpoints_changed()
end end
############################################################# #############################################################
@ -1412,13 +1458,15 @@ class Matter_Device
def clean_remotes() def clean_remotes()
import introspect import introspect
# print("clean_remotes", self.http_remotes)
# init all remotes with count 0 # init all remotes with count 0
if self.http_remotes if self.http_remotes # tests if `self.http_remotes` is not `nil` and not empty
var remotes_map = {} # key: remote object, value: count of references var remotes_map = {} # key: remote object, value: count of references
for http_remote: self.http_remotes for http_remote: self.http_remotes
remotes_map[http_remote] = 0 remotes_map[http_remote] = 0
end end
# print("remotes_map", remotes_map)
# scan all endpoints # scan all endpoints
for pi: self.plugins for pi: self.plugins
@ -1428,16 +1476,23 @@ class Matter_Device
end end
end end
# print("remotes_map2", remotes_map)
# tasmota.log("MTR: remotes references: " + str(remotes_map), 3) # tasmota.log("MTR: remotes references: " + str(remotes_map), 3)
var remote_to_remove = [] # we first get the list of remotes to remove, to not interfere with map iterator
for remote:remotes_map.keys() for remote:remotes_map.keys()
if remotes_map[remote] == 0 if remotes_map[remote] == 0
# remove remote_to_remove.push(remote)
tasmota.log("MTR: remove unused remote: " + remote.addr, 3)
remote.close()
self.http_remotes.remove(remote)
end end
end end
for remote: remote_to_remove
tasmota.log("MTR: remove unused remote: " + remote.addr, 3)
remote.close()
self.http_remotes.remove(remote.addr)
end
end end
end end

View File

@ -96,7 +96,7 @@ class Matter_Expirable
# set relative time in the future for expiration (in seconds) # set relative time in the future for expiration (in seconds)
def set_expire_in_seconds(s, now) def set_expire_in_seconds(s, now)
if s == nil return end if s == nil return end
if now == nil now = tasmota.rtc()['utc'] end if now == nil now = tasmota.rtc_utc() end
self.set_expire_time(now + s) self.set_expire_time(now + s)
end end
@ -104,7 +104,7 @@ class Matter_Expirable
# set relative time in the future for expiration (in seconds) # set relative time in the future for expiration (in seconds)
# returns `true` if expiration date has been reached # returns `true` if expiration date has been reached
def has_expired(now) def has_expired(now)
if now == nil now = tasmota.rtc()['utc'] end if now == nil now = tasmota.rtc_utc() end
if self._expiration != nil if self._expiration != nil
return now >= self._expiration return now >= self._expiration
end end

View File

@ -75,7 +75,7 @@ class Matter_Fabric : Matter_Expirable
self._store = store self._store = store
self._sessions = matter.Expirable_list() self._sessions = matter.Expirable_list()
self.fabric_label = "" self.fabric_label = ""
self.created = tasmota.rtc()['utc'] self.created = tasmota.rtc_utc()
# init group counters # init group counters
self._counter_group_data_snd_impl = matter.Counter() self._counter_group_data_snd_impl = matter.Counter()
self._counter_group_ctrl_snd_impl = matter.Counter() self._counter_group_ctrl_snd_impl = matter.Counter()
@ -247,7 +247,9 @@ class Matter_Fabric : Matter_Expirable
def add_session(s) def add_session(s)
if self._sessions.find(s) == nil if self._sessions.find(s) == nil
while size(self._sessions) >= self._MAX_CASE while size(self._sessions) >= self._MAX_CASE
self._sessions.remove(self._sessions.find(self.get_oldest_session())) var session_deleted = self.get_oldest_session()
self._sessions.remove(self._sessions.find(session_deleted))
self._store.remove_session(session_deleted)
end end
self._sessions.push(s) self._sessions.push(s)
end end

View File

@ -177,7 +177,7 @@ class Matter_HTTP_remote : Matter_HTTP_async
end end
# reduce the update time after a read is succesful # reduce the update time after a read is succesful
self.change_schedule(self.UPDATE_CMD0, self.UPDATE_TIME2) self.change_schedule(self.UPDATE_CMD5, self.UPDATE_TIME2)
end end
if changed self.info_changed() end if changed self.info_changed() end
@ -189,7 +189,7 @@ class Matter_HTTP_remote : Matter_HTTP_async
if alive if alive
# device is known to be reachable # device is known to be reachable
self.reachable = true self.reachable = true
self.reachable_utc = tasmota.rtc()['utc'] self.reachable_utc = tasmota.rtc_utc()
else else
self.reachable = false self.reachable = false
end end
@ -344,7 +344,7 @@ class Matter_HTTP_remote : Matter_HTTP_async
var seconds = -1 # default if no known value var seconds = -1 # default if no known value
if self.reachable_utc != nil if self.reachable_utc != nil
seconds = tasmota.rtc()['utc'] - self.reachable_utc seconds = tasmota.rtc_utc() - self.reachable_utc
end end
return matter.seconds_to_dhm(seconds) return matter.seconds_to_dhm(seconds)
end end

View File

@ -28,30 +28,53 @@ class Matter_IM
var device var device
var subs_shop # subscriptions shop var subs_shop # subscriptions shop
var send_queue # list of IM_Message queued for sending as part of exchange-id var send_queue # list of IM_Message queued for sending as part of exchange-id
var read_request_solo # instance of ReadRequestMessage_solo to optimize single reads
var invoke_request_solo # instance of InvokeRequestMessage_solo to optimize single reads
var tlv_solo # instance of Matter_TLV_item for simple responses
def init(device) def init(device)
self.device = device self.device = device
self.send_queue = [] self.send_queue = []
self.subs_shop = matter.IM_Subscription_Shop(self) self.subs_shop = matter.IM_Subscription_Shop(self)
self.read_request_solo = matter.ReadRequestMessage_solo()
self.invoke_request_solo = matter.InvokeRequestMessage_solo()
self.tlv_solo = matter.TLV.Matter_TLV_item()
end end
def process_incoming(msg) def process_incoming(msg)
# messages are always TLV, decode payload
# tasmota.log("MTR: received IM message " + matter.inspect(msg), 3)
var opcode = msg.opcode
# Fast-Track processing
# first pass is optimized for simple frequent messages and avoids complete decoding of TLV
if opcode == 0x02 # Read Request
var read_request_solo = self.read_request_solo.from_raw(msg.raw, msg.app_payload_idx)
if read_request_solo != nil
# tasmota.log(f"MTR: process_incoming {read_request_solo=}")
return self.process_read_request_solo(msg, read_request_solo)
end
elif opcode == 0x08 # Invoke Request
var invoke_request_solo = self.invoke_request_solo.from_raw(msg.raw, msg.app_payload_idx)
# tasmota.log(f"MTR: {invoke_request_solo=} {msg.raw[msg.app_payload_idx .. ].tohex()} {msg.app_payload_idx=} {msg.raw.tohex()}")
if invoke_request_solo != nil
return self.process_invoke_request_solo(msg, invoke_request_solo)
end
end
# tasmota.log("MTR: received IM message " + matter.inspect(msg), 3)
var val = matter.TLV.parse(msg.raw, msg.app_payload_idx) var val = matter.TLV.parse(msg.raw, msg.app_payload_idx)
# tasmota.log("MTR: IM TLV: " + str(val), 3) # tasmota.log("MTR: IM TLV: " + str(val), 3)
var InteractionModelRevision = val.findsubval(0xFF) # var InteractionModelRevision = val.findsubval(0xFF)
# tasmota.log("MTR: InteractionModelRevision=" + (InteractionModelRevision != nil ? str(InteractionModelRevision) : "nil"), 4) # tasmota.log("MTR: InteractionModelRevision=" + (InteractionModelRevision != nil ? str(InteractionModelRevision) : "nil"), 4)
var opcode = msg.opcode
if opcode == 0x01 # Status Response if opcode == 0x01 # Status Response
return self.process_status_response(msg, val) return self.process_status_response(msg, val)
elif opcode == 0x02 # Read Request elif opcode == 0x02 # Read Request
self.send_ack_now(msg) # self.send_ack_now(msg) # to improve latency, we don't automatically Ack on invoke request
return self.process_read_request(msg, val) return self.process_read_request(msg, val)
elif opcode == 0x03 # Subscribe Request elif opcode == 0x03 # Subscribe Request
self.send_ack_now(msg) self.send_ack_now(msg)
@ -66,7 +89,7 @@ class Matter_IM
elif opcode == 0x07 # Write Response elif opcode == 0x07 # Write Response
return self.process_write_response(msg, val) return self.process_write_response(msg, val)
elif opcode == 0x08 # Invoke Request elif opcode == 0x08 # Invoke Request
self.send_ack_now(msg) # self.send_ack_now(msg) # to improve latency, we don't automatically Ack on invoke request
return self.process_invoke_request(msg, val) return self.process_invoke_request(msg, val)
elif opcode == 0x09 # Invoke Response elif opcode == 0x09 # Invoke Response
return self.process_invoke_response(msg, val) return self.process_invoke_response(msg, val)
@ -97,6 +120,7 @@ class Matter_IM
# #
# returns `true` if packet could be sent # returns `true` if packet could be sent
def send_ack_now(msg) def send_ack_now(msg)
if msg == nil return end
msg.session._message_handler.send_encrypted_ack(msg, false #-not reliable-#) msg.session._message_handler.send_encrypted_ack(msg, false #-not reliable-#)
end end
@ -201,7 +225,7 @@ class Matter_IM
# Inner code shared between read_attributes and subscribe_request # Inner code shared between read_attributes and subscribe_request
# #
# query: `ReadRequestMessage` or `SubscribeRequestMessage` # query: `ReadRequestMessage` or `SubscribeRequestMessage`
def _inner_process_read_request(session, query, no_log) def _inner_process_read_request(session, query, msg, no_log)
### Inner function to be iterated upon ### Inner function to be iterated upon
# ret is the ReportDataMessage list to send back # ret is the ReportDataMessage list to send back
@ -215,48 +239,55 @@ class Matter_IM
var TLV = matter.TLV var TLV = matter.TLV
var attr_name = matter.get_attribute_name(ctx.cluster, ctx.attribute) var attr_name = matter.get_attribute_name(ctx.cluster, ctx.attribute)
attr_name = attr_name ? " (" + attr_name + ")" : "" attr_name = attr_name ? " (" + attr_name + ")" : ""
# Special case to report unsupported item, if pi==nil # Special case to report unsupported item, if pi==nil
var res = (pi != nil) ? pi.read_attribute(session, ctx) : nil var res = (pi != nil) ? pi.read_attribute(session, ctx, self.tlv_solo) : nil
var found = true # stop expansion since we have a value var found = true # stop expansion since we have a value
var a1_raw # this is the bytes() block we need to add to response (or nil) var a1_raw_or_list # contains either a bytes() buffer to append, or a list of bytes(), or nil
if res != nil if res != nil
var res_str = str(res) # get the value with anonymous tag before it is tagged, for logging var res_str = ""
if !no_log
res_str = res.to_str_val() # get the value with anonymous tag before it is tagged, for logging
end
var a1 = matter.AttributeReportIB() # check if too big to encode as a single packet
a1.attribute_data = matter.AttributeDataIB() if (res.is_list || res.is_array) && res.encode_len() > matter.IM_ReportData.MAX_MESSAGE
a1.attribute_data.data_version = 1 # tasmota.log(f"MTR: >>>>>> long response", 3)
a1.attribute_data.path = matter.AttributePathIB() a1_raw_or_list = [] # we return a list of block
a1.attribute_data.path.endpoint = ctx.endpoint var a1_raw = bytes(48)
a1.attribute_data.path.cluster = ctx.cluster var empty_list = TLV.Matter_TLV_array()
a1.attribute_data.path.attribute = ctx.attribute self.attributedata2raw(a1_raw, ctx, empty_list, false)
a1.attribute_data.data = res a1_raw_or_list.push(a1_raw)
# tasmota.log(f"MTR: >>>>>> long response global DELETE {a1_raw.tohex()}", 3)
var a1_tlv = a1.to_TLV() for elt:res.val
var a1_len = a1_tlv.encode_len() a1_raw = bytes(48)
var a1_bytes = bytes(a1_len) # pre-size bytes() to the actual size # var list_item = TLV.Matter_TLV_array()
a1_raw = a1_tlv.tlv2raw(a1_bytes) # list_item.val.push(elt)
# tasmota.log(format("MTR: guessed len=%i actual=%i '%s'", a1_len, size(a1_raw), a1_raw.tohex()), 2) self.attributedata2raw(a1_raw, ctx, elt, true #- add ListIndex:null -#)
# tasmota.log(f"MTR: >>>>>> long response global ADD {a1_raw.tohex()}", 3)
a1_raw_or_list.push(a1_raw)
end
# tasmota.log(f"MTR: >>>>>> long response global {a1_raw_or_list}", 3)
else
# normal encoding
# encode directly raw bytes()
a1_raw_or_list = bytes(48) # pre-reserve 48 bytes
self.attributedata2raw(a1_raw_or_list, ctx, res)
end
if !no_log if !no_log
tasmota.log(format("MTR: >Read_Attr (%6i) %s%s - %s", session.local_session_id, str(ctx), attr_name, res_str), 3) tasmota.log(f"MTR: >Read_Attr ({session.local_session_id:6i}) {ctx}{attr_name} - {res_str}", 3)
end end
elif ctx.status != nil elif ctx.status != nil
if direct # we report an error only if a concrete direct read, not with wildcards if direct # we report an error only if a concrete direct read, not with wildcards
var a1 = matter.AttributeReportIB() # encode directly raw bytes()
a1.attribute_status = matter.AttributeStatusIB() a1_raw_or_list = bytes(48) # pre-reserve 48 bytes
a1.attribute_status.path = matter.AttributePathIB() self.attributestatus2raw(a1_raw_or_list, ctx, ctx.status)
a1.attribute_status.status = matter.StatusIB()
a1.attribute_status.path.endpoint = ctx.endpoint
a1.attribute_status.path.cluster = ctx.cluster
a1.attribute_status.path.attribute = ctx.attribute
a1.attribute_status.status.status = ctx.status
var a1_tlv = a1.to_TLV() if tasmota.loglevel(3)
var a1_len = a1_tlv.encode_len() tasmota.log(format("MTR: >Read_Attr (%6i) %s%s - STATUS: 0x%02X %s", session.local_session_id, str(ctx), attr_name, ctx.status, ctx.status == matter.UNSUPPORTED_ATTRIBUTE ? "UNSUPPORTED_ATTRIBUTE" : ""), 3)
var a1_bytes = bytes(a1_len) # pre-size bytes() to the actual size end
a1_raw = a1_tlv.tlv2raw(a1_bytes)
tasmota.log(format("MTR: >Read_Attr (%6i) %s%s - STATUS: 0x%02X %s", session.local_session_id, str(ctx), attr_name, ctx.status, ctx.status == matter.UNSUPPORTED_ATTRIBUTE ? "UNSUPPORTED_ATTRIBUTE" : ""), 3)
end end
else else
tasmota.log(format("MTR: >Read_Attr (%6i) %s%s - IGNORED", session.local_session_id, str(ctx), attr_name), 3) tasmota.log(format("MTR: >Read_Attr (%6i) %s%s - IGNORED", session.local_session_id, str(ctx), attr_name), 3)
@ -264,27 +295,55 @@ class Matter_IM
found = false found = false
end end
# check if we still have enough room in last block # a1_raw_or_list if either nil, bytes(), of list(bytes())
if a1_raw # do we have bytes to add, and it's not zero size var idx = isinstance(a1_raw_or_list, list) ? 0 : nil # index in list, or nil if non-list
while a1_raw_or_list != nil
var elt = (idx == nil) ? a1_raw_or_list : a1_raw_or_list[idx] # dereference
if size(ret.attribute_reports) == 0 if size(ret.attribute_reports) == 0
ret.attribute_reports.push(a1_raw) # push raw binary instead of a TLV ret.attribute_reports.push(elt) # push raw binary instead of a TLV
else # already blocks present, see if we can add to the latest, or need to create a new block else # already blocks present, see if we can add to the latest, or need to create a new block
var last_block = ret.attribute_reports[-1] var last_block = ret.attribute_reports[-1]
if size(last_block) + size(a1_raw) <= matter.IM_ReportData.MAX_MESSAGE if size(last_block) + size(elt) <= matter.IM_ReportData.MAX_MESSAGE
# add to last block # add to last block
last_block .. a1_raw last_block .. elt
else else
ret.attribute_reports.push(a1_raw) # push raw binary instead of a TLV ret.attribute_reports.push(elt) # push raw binary instead of a TLV
end
end
if idx == nil
a1_raw_or_list = nil # stop loop
else
idx += 1
if idx >= size(a1_raw_or_list)
a1_raw_or_list = nil # stop loop
end end
end end
end end
# check if we still have enough room in last block
# if a1_raw_or_list # do we have bytes to add, and it's not zero size
# if size(ret.attribute_reports) == 0
# ret.attribute_reports.push(a1_raw_or_list) # push raw binary instead of a TLV
# else # already blocks present, see if we can add to the latest, or need to create a new block
# var last_block = ret.attribute_reports[-1]
# if size(last_block) + size(a1_raw_or_list) <= matter.IM_ReportData.MAX_MESSAGE
# # add to last block
# last_block .. a1_raw_or_list
# else
# ret.attribute_reports.push(a1_raw_or_list) # push raw binary instead of a TLV
# end
# end
# end
return found # return true if we had a match return found # return true if we had a match
end end
var endpoints = self.device.get_active_endpoints() var endpoints = self.device.get_active_endpoints()
# structure is `ReadRequestMessage` 10.6.2 p.558 # structure is `ReadRequestMessage` 10.6.2 p.558
var ctx = matter.Path() var ctx = matter.Path()
ctx.msg = msg
# prepare the response # prepare the response
var ret = matter.ReportDataMessage() var ret = matter.ReportDataMessage()
@ -296,6 +355,7 @@ class Matter_IM
ctx.endpoint = q.endpoint ctx.endpoint = q.endpoint
ctx.cluster = q.cluster ctx.cluster = q.cluster
ctx.attribute = q.attribute ctx.attribute = q.attribute
ctx.fabric_filtered = query.fabric_filtered
ctx.status = matter.UNSUPPORTED_ATTRIBUTE #default error if returned `nil` ctx.status = matter.UNSUPPORTED_ATTRIBUTE #default error if returned `nil`
# expand endpoint # expand endpoint
@ -322,6 +382,290 @@ class Matter_IM
return ret return ret
end end
#############################################################
# path2raw
#
# Encodes endpoint/cluster/attribute as `AttributePathIB` elements
# Takes sub-tag
#
# 1 = AttributePathIB
# 0 = EnableTagCompression bool opt
# 1 = Node
# 2 = Endpoint
# 3 = Cluste
# 4 = Attribute
# 5 = ListIndex (opt)
#
# 3701 1 = LIST
# 2402 01 2 = 1U (U1)
# 2403 39 3 = 0x39U (U1)
# 2404 11 4 = 0x11U (U1)
# [OPTIONAL ListIndex]
# 3405 5 = NULL
# 18
def path2raw(raw, ctx, sub_tag, list_index_null)
# open struct
raw.add(0x37, 1) # add 37
raw.add(sub_tag, 1) # add sub_tag
# add endpoint
if ctx.endpoint <= 0xFF # endpoint is 16 bits max
raw.add(0x2402, -2) # add 2402
raw.add(ctx.endpoint, 1)
else
raw.add(0x2502, -2) # add 2502
raw.add(ctx.endpoint, 2)
end
# add cluster
if ctx.cluster <= 0xFF # cluster is 32 bits max
raw.add(0x2403, -2) # add 2403
raw.add(ctx.cluster, 1)
elif ctx.cluster <= 0xFFFF
raw.add(0x2503, -2) # add 2503
raw.add(ctx.cluster, 2)
else
raw.add(0x2603, -2) # add 2603
raw.add(ctx.cluster, 4)
end
# add attribute
if ctx.attribute <= 0xFF # cluster is 32 bits max
raw.add(0x2404, -2) # add 2404
raw.add(ctx.attribute, 1)
elif ctx.attribute <= 0xFFFF
raw.add(0x2504, -2) # add 2504
raw.add(ctx.attribute, 2)
else
raw.add(0x2604, -2) # add 2604
raw.add(ctx.attribute, 4)
end
# do we add ListIndex: null
if list_index_null
raw.add(0x3405, -2) # add 3405
end
# close
raw.add(0x18, 1) # add 18
end
#############################################################
# attributedata2raw
#
# generate a raw version of AttributeDataIB
#
# Typical answer
#
# AttributeReportIB
# 0 = AttributeStatusIB
# 1 = AttributeDataIB
# 0 = DataVersion U1
# 1 = AttributePathIB
# 0 = EnableTagCompression bool opt
# 1 = Node
# 2 = Endpoint
# 3 = Cluste
# 4 = Attribute
# 5 = ListIndex (opt)
# 2 = Data
#
# 153601.15350124000137012402012403392404111829021818.1824FF0118
# 15350124000137012402012403392404111829021818
# 1535012400013701
# 240201240339240411
# 1829021818
#
# 15
# 3501 1 = {}
# 2400 01 0 = 1U (U1)
# 3701 1 = LIST
# 2402 01 2 = 1U (U1)
# 2403 39 3 = 0x39U (U1)
# 2404 11 4 = 0x11U (U1)
# [OPTIONAL ListIndex]
# 3405 5 = NULL
#
# 18
# 2902 2 = True
# 18
# 18
def attributedata2raw(raw, ctx, val, list_index_null)
raw.add(0x15350124, -4) # add 15350124
raw.add(0x0001, -2) # add 0001
self.path2raw(raw, ctx, 0x01, list_index_null)
# add value with tag 2
val.tag_sub = 2
val.tlv2raw(raw)
# close 2 structs
raw.add(0x1818, -2)
end
#############################################################
# attributedata2raw
#
# generate a raw version of AttributeStatusIB
#
#
# Typical answer
#
# AttributeReportIB
# 0 = AttributeStatusIB
# 0 = AttributePathIB
# 0 = EnableTagCompression bool opt
# 1 = Node
# 2 = Endpoint
# 3 = Cluste
# 4 = Attribute
# 5 = ListIndex (opt)
# 1 = StatusIB
# 0 = Status (u1)
# 1 = ClusterStatus (u1)
# 1 = AttributeDataIB
#
# 15360115350037002402012403022404031835012400041818181824FF0118
# 153601 1535003700 - 2402012403022404031835012400041818181824FF0118
#
# 15
# 3601
#
# 15
# 3500 0 = struct
# 3700 0 = list
# 240201 2 = 1U endpoint
# 240302 3 = 2U cluster
# 240403 4 = 3U attribute
# 18
# 3501 1 = struct
# 240004 0 = 4U status
# 18
# 18
# 18
#
# 18
# 24FF01
# 18
def attributestatus2raw(raw, ctx, status)
raw.add(0x15, 1) # add 15
raw.add(0x3500, -2) # add 3500
self.path2raw(raw, ctx, 0x00)
raw.add(0x3501, -2) # add 3501 for status
# status
if ctx.status <= 255
raw.add(0x2400, -2) # add 2400
raw.add(ctx.status, 1)
else
raw.add(0x2500, -2) # add 2500
raw.add(ctx.status, 2)
end
# close
raw.add(0x1818, -2) # add 1818
raw.add(0x18, 1) # add 18
end
#############################################################
# attributedata2raw
#
# generate a raw version of InvokeResponseIB()
# Typical answer
#
# 1535013700240011240122240244183501240000181818
#
# 0 = CommandDataIB
# 0 = CommandPathIB
# 0 = endpoint u2
# 1 = cluster u4
# 2 = command u4
# 1 = <fields>
# 1 = CommandStatusIB
# 0 = CommandPathIB
# 0 = endpoint u2
# 1 = cluster u4
# 2 = command u4
# 1 = StatusIB
# 0 = status u1
# 1 = ClusterStatus u1
#
# 1535013700240011240122240244183501240000181818
# 15
# 3501 1 = struct
# 3700 0 = list
# 240011 0 = endpoint
# 240122 1 = cluster
# 240244 2 = command
# 18
# 3501 1 = struct
# 240000 0 = 0 (status)
# 18
# 18
# 18
#
# 1535003700240011240122240244182401031818
# 15
# 3500 0 = struct
# 3700 0 = list
# 240011 0 = endpoint
# 240122 1 = cluster
# 240244 2 = command
# 18
# 240103 1 = <field>
# 18
# 18
def invokeresponse2raw(raw, ctx, val)
raw.add(0x15, 1) # add 15
if val == nil
raw.add(0x3501, -2) # add 3500
else
raw.add(0x3500, -2) # add 3500
end
raw.add(0x3700, -2) # add 3700
# add endpoint
if ctx.endpoint <= 0xFF # endpoint is 16 bits max
raw.add(0x2400, -2) # add 2400
raw.add(ctx.endpoint, 1)
else
raw.add(0x2500, -2) # add 2500
raw.add(ctx.endpoint, 2)
end
# add cluster
if ctx.cluster <= 0xFF # cluster is 32 bits max
raw.add(0x2401, -2) # add 2401
raw.add(ctx.cluster, 1)
elif ctx.cluster <= 0xFFFF
raw.add(0x2501, -2) # add 2501
raw.add(ctx.cluster, 2)
else
raw.add(0x2601, -2) # add 2601
raw.add(ctx.cluster, 4)
end
# add attribute
if ctx.command <= 0xFF # cluster is 32 bits max
raw.add(0x2402, -2) # add 2402
raw.add(ctx.command, 1)
elif ctx.command <= 0xFFFF
raw.add(0x2502, -2) # add 2502
raw.add(ctx.command, 2)
else
raw.add(0x2602, -2) # add 2602
raw.add(ctx.command, 4)
end
raw.add(0x18, 1) # add 18
# either value or statuc
if val == nil
var status = ctx.status
if status == nil status = matter.SUCCESS end
raw.add(0x3501, -2) # add 3501
raw.add(0x2400, -2) # add 2400
raw.add(ctx.status, 1) # add status:1
raw.add(0x18, 1) # add 18
else
val.tag_sub = 1 # set sub_tag for reponse
val.tlv2raw(raw)
end
# close
raw.add(0x1818, -2) # add 1818
end
############################################################# #############################################################
# process IM 0x02 Read Request # process IM 0x02 Read Request
# #
@ -329,15 +673,130 @@ class Matter_IM
# returns `true` if processed, `false` if silently ignored, # returns `true` if processed, `false` if silently ignored,
# or raises an exception # or raises an exception
def process_read_request(msg, val) def process_read_request(msg, val)
matter.profiler.log("read_request_start")
# matter.profiler.log(str(val))
var query = matter.ReadRequestMessage().from_TLV(val) var query = matter.ReadRequestMessage().from_TLV(val)
# matter.profiler.log(str(query))
if query.attributes_requests != nil if query.attributes_requests != nil
var ret = self._inner_process_read_request(msg.session, query) var ret = self._inner_process_read_request(msg.session, query, msg)
self.send_report_data(msg, ret) self.send_report_data(msg, ret)
end end
return true return true
end end
#############################################################
# process IM 0x02 Read Request
#
# val is the TLV structure
# returns `true` if processed, `false` if silently ignored,
# or raises an exception
def process_read_request_solo(msg, ctx)
# prepare fallback error
ctx.status = matter.INVALID_ACTION
ctx.msg = msg
# find pi for this endpoint/cluster/attribute
var pi = self.device.process_attribute_read_solo(ctx)
var res = nil
# matter.profiler.log("read_request_solo pi ok")
# tasmota.log(f"MTR: process_read_request_solo {pi=}")
var raw # this is the bytes() block we need to add to response (or nil)
if pi != nil
ctx.status = matter.UNSUPPORTED_ATTRIBUTE # new fallback error
res = pi.read_attribute(msg.session, ctx, self.tlv_solo)
end
matter.profiler.log("read_request_solo read done")
if res != nil
# check if the payload is a complex structure and too long to fit in a single response packet
if (res.is_list || res.is_array) && res.encode_len() > matter.IM_ReportData.MAX_MESSAGE
# revert to standard
# the attribute will be read again, but it's hard to avoid it
res = nil # indicated to GC that we don't need it again
tasmota.log(f"MTR: Response to big, revert to non-solo", 3)
var val = matter.TLV.parse(msg.raw, msg.app_payload_idx)
return self.process_read_request(msg, val)
end
# encode directly raw bytes()
raw = bytes(48) # pre-reserve 48 bytes
raw.add(0x15, 1) # add 15
raw.add(0x3601, -2) # add 3601
self.attributedata2raw(raw, ctx, res)
# add suffix 1824FF0118
raw.add(0x1824FF01, -4) # add 1824FF01
raw.add(0x18, 1) # add 18
elif ctx.status != nil
# encode directly raw bytes()
raw = bytes(48) # pre-reserve 48 bytes
raw.add(0x15, 1) # add 15
raw.add(0x3601, -2) # add 3601
self.attributestatus2raw(raw, ctx, ctx.status)
# add suffix 1824FF0118
raw.add(0x1824FF01, -4) # add 1824FF01
raw.add(0x18, 1) # add 18
else
tasmota.log(f"MTR: >Read_Attr ({msg.session.local_session_id:6i}) {ctx} - IGNORED", 3)
return false
end
# send packet
var resp = msg.build_response(0x05 #-Report Data-#, true)
var responder = self.device.message_handler
var msg_raw = msg.raw
msg_raw.clear()
resp.encode_frame(raw, msg_raw) # payload in cleartext
resp.encrypt()
if tasmota.loglevel(4)
tasmota.log(format("MTR: <snd (%6i) id=%i exch=%i rack=%s", resp.session.local_session_id, resp.message_counter, resp.exchange_id, resp.ack_message_counter), 4)
end
responder.send_response_frame(resp)
# postpone lengthy operations after sending back response
matter.profiler.log("RESPONSE SENT")
var attr_name = matter.get_attribute_name(ctx.cluster, ctx.attribute)
attr_name = attr_name ? " (" + attr_name + ")" : ""
if res != nil
var res_str = res.to_str_val() # get the value with anonymous tag before it is tagged, for logging
if tasmota.loglevel(3)
tasmota.log(f"MTR: >Read_Attr1({msg.session.local_session_id:6i}) {ctx}{attr_name} - {res_str}", 3)
end
# if matter.profiler.active && tasmota.loglevel(3)
# tasmota.log(f"MTR: {raw=}", 3) # TODO remove before flight
# end
elif ctx.status != nil
var unsupported_attribute = (ctx.status == matter.UNSUPPORTED_ATTRIBUTE ? "UNSUPPORTED_ATTRIBUTE" : "")
if tasmota.loglevel(3)
tasmota.log(f"MTR: >Read_Attr1({msg.session.local_session_id:6i}) {ctx}{attr_name} - STATUS: 0x{ctx.status:02X} {unsupported_attribute}", 3)
end
# if matter.profiler.active && tasmota.loglevel(3)
# tasmota.log(f"MTR: {raw=}", 3) # TODO remove before flight
# end
else
if tasmota.loglevel(3)
tasmota.log(f"MTR: >Read_Attr1({msg.session.local_session_id:6i}) {ctx}{attr_name} - IGNORED", 3)
end
end
# matter.profiler.log("read_request_solo end")
return true
end
############################################################# #############################################################
# process IM 0x03 Subscribe Request # process IM 0x03 Subscribe Request
# #
@ -355,16 +814,20 @@ class Matter_IM
# expand a string with all attributes requested # expand a string with all attributes requested
var attr_req = [] var attr_req = []
var ctx = matter.Path() var ctx = matter.Path()
ctx.msg = msg
for q:query.attributes_requests for q:query.attributes_requests
ctx.endpoint = q.endpoint ctx.endpoint = q.endpoint
ctx.cluster = q.cluster ctx.cluster = q.cluster
ctx.attribute = q.attribute ctx.attribute = q.attribute
attr_req.push(str(ctx)) attr_req.push(str(ctx))
end end
tasmota.log(format("MTR: >Subscribe (%6i) %s (min=%i, max=%i, keep=%i) sub=%i", tasmota.log(format("MTR: >Subscribe (%6i) %s (min=%i, max=%i, keep=%i) sub=%i fabric_filtered=%s",
msg.session.local_session_id, attr_req.concat(" "), sub.min_interval, sub.max_interval, query.keep_subscriptions ? 1 : 0, sub.subscription_id), 3) msg.session.local_session_id, attr_req.concat(" "), sub.min_interval, sub.max_interval, query.keep_subscriptions ? 1 : 0, sub.subscription_id, query.fabric_filtered), 3)
if query.event_requests != nil && size(query.event_requests) > 0
tasmota.log(f"MTR: >Subscribe (%6i) event_requests_size={size(query.event_requests)}", 3)
end
var ret = self._inner_process_read_request(msg.session, query, true #-no_log-#) var ret = self._inner_process_read_request(msg.session, query, msg, true #-no_log-#)
# ret is of type `Matter_ReportDataMessage` # ret is of type `Matter_ReportDataMessage`
ret.subscription_id = sub.subscription_id # enrich with subscription id TODO ret.subscription_id = sub.subscription_id # enrich with subscription id TODO
self.send_subscribe_response(msg, ret, sub) self.send_subscribe_response(msg, ret, sub)
@ -378,9 +841,12 @@ class Matter_IM
# returns `true` if processed, `false` if silently ignored, # returns `true` if processed, `false` if silently ignored,
# or raises an exception # or raises an exception
def process_invoke_request(msg, val) def process_invoke_request(msg, val)
# import debug
# structure is `ReadRequestMessage` 10.6.2 p.558 # structure is `ReadRequestMessage` 10.6.2 p.558
# tasmota.log("MTR: IM:invoke_request processing start", 4) # tasmota.log("MTR: IM:invoke_request processing start", 4)
matter.profiler.log("invoke_request_start")
var ctx = matter.Path() var ctx = matter.Path()
ctx.msg = msg
var query = matter.InvokeRequestMessage().from_TLV(val) var query = matter.InvokeRequestMessage().from_TLV(val)
if query.invoke_requests != nil if query.invoke_requests != nil
@ -398,52 +864,44 @@ class Matter_IM
var cmd_name = matter.get_command_name(ctx.cluster, ctx.command) var cmd_name = matter.get_command_name(ctx.cluster, ctx.command)
var ctx_str = str(ctx) # keep string before invoking, it is modified by response var ctx_str = str(ctx) # keep string before invoking, it is modified by response
var res = self.device.invoke_request(msg.session, q.command_fields, ctx) var res = self.device.invoke_request(msg.session, q.command_fields, ctx)
matter.profiler.log("COMMAND DONE")
var params_log = (ctx.log != nil) ? "(" + str(ctx.log) + ") " : "" var params_log = (ctx.log != nil) ? "(" + str(ctx.log) + ") " : ""
tasmota.log(format("MTR: >Command (%6i) %s %s %s", msg.session.local_session_id, ctx_str, cmd_name ? cmd_name : "", params_log), ctx.endpoint != 0 ? 2 : 3 #- don't log for endpoint 0 -# ) tasmota.log(format("MTR: >Command (%6i) %s %s %s", msg.session.local_session_id, ctx_str, cmd_name ? cmd_name : "", params_log), ctx.endpoint != 0 ? 2 : 3 #- don't log for endpoint 0 -# )
# tasmota.log("MTR: Perf/Command = " + str(debug.counters()), 4)
ctx.log = nil ctx.log = nil
var a1 = matter.InvokeResponseIB() var raw = bytes(32)
# var a1 = matter.InvokeResponseIB()
if res == true || ctx.status == matter.SUCCESS # special case, just respond ok if res == true || ctx.status == matter.SUCCESS # special case, just respond ok
a1.status = matter.CommandStatusIB() ctx.status = matter.SUCCESS
a1.status.command_path = matter.CommandPathIB() self.invokeresponse2raw(raw, ctx, nil)
a1.status.command_path.endpoint = ctx.endpoint ret.invoke_responses.push(raw)
a1.status.command_path.cluster = ctx.cluster if tasmota.loglevel(3)
a1.status.command_path.command = ctx.command tasmota.log(f"MTR: <Replied ({msg.session.local_session_id:6i}) OK exch={msg.exchange_id:i}", 3)
a1.status.status = matter.StatusIB() end
a1.status.status.status = matter.SUCCESS
ret.invoke_responses.push(a1)
tasmota.log(format("MTR: <Replied (%6i) OK exch=%i", msg.session.local_session_id, msg.exchange_id), 3)
elif res != nil elif res != nil
a1.command = matter.CommandDataIB() self.invokeresponse2raw(raw, ctx, res)
a1.command.command_path = matter.CommandPathIB() ret.invoke_responses.push(raw)
a1.command.command_path.endpoint = ctx.endpoint
a1.command.command_path.cluster = ctx.cluster
a1.command.command_path.command = ctx.command
a1.command.command_fields = res
ret.invoke_responses.push(a1)
cmd_name = matter.get_command_name(ctx.cluster, ctx.command) cmd_name = matter.get_command_name(ctx.cluster, ctx.command)
tasmota.log(format("MTR: <Replied (%6i) %s %s", msg.session.local_session_id, str(ctx), cmd_name ? cmd_name : ""), 3) if !cmd_name cmd_name = "" end
if tasmota.loglevel(3)
tasmota.log(f"MTR: <Replied ({msg.session.local_session_id:6i}) {ctx} {cmd_name}", 3)
end
elif ctx.status != nil elif ctx.status != nil
a1.status = matter.CommandStatusIB() self.invokeresponse2raw(raw, ctx, nil)
a1.status.command_path = matter.CommandPathIB() ret.invoke_responses.push(raw)
a1.status.command_path.endpoint = ctx.endpoint if tasmota.loglevel(3)
a1.status.command_path.cluster = ctx.cluster tasmota.log(f"MTR: <Replied ({msg.session.local_session_id:6i}) Status=0x{ctx.status:02X} exch={msg.exchange_id:i}", 3)
a1.status.command_path.command = ctx.command end
a1.status.status = matter.StatusIB()
a1.status.status.status = ctx.status
ret.invoke_responses.push(a1)
tasmota.log(format("MTR: <Replied (%6i) Status=0x%02X exch=%i", msg.session.local_session_id, ctx.status, msg.exchange_id), 3)
else else
tasmota.log(format("MTR: _Ignore (%6i) exch=%i", msg.session.local_session_id, msg.exchange_id), 3) if tasmota.loglevel(3)
tasmota.log(f"MTR: _Ignore ({msg.session.local_session_id:6i}) exch={msg.exchange_id:i}", 3)
end
# ignore if content is nil and status is undefined # ignore if content is nil and status is undefined
end end
end end
# tasmota.log("MTR: invoke_responses="+str(ret.invoke_responses), 4)
if size(ret.invoke_responses) > 0 if size(ret.invoke_responses) > 0
# tasmota.log("MTR: InvokeResponse=" + str(ret), 4)
# tasmota.log("MTR: InvokeResponseTLV=" + str(ret.to_TLV()), 3)
self.send_invoke_response(msg, ret) self.send_invoke_response(msg, ret)
else else
return false # we don't send anything, hence the responder sends a simple packet ack return false # we don't send anything, hence the responder sends a simple packet ack
@ -452,6 +910,78 @@ class Matter_IM
end end
end end
#############################################################
# process IM 0x08 Invoke Request
#
# val is the TLV structure
# returns `true` if processed, `false` if silently ignored,
# or raises an exception
def process_invoke_request_solo(msg, ctx)
# import debug
matter.profiler.log("invoke_request_solo_start")
ctx.msg = msg
ctx.status = matter.UNSUPPORTED_COMMAND #default error if returned `nil`
var cmd_name = matter.get_command_name(ctx.cluster, ctx.command)
var ctx_str = str(ctx) # keep string before invoking, it is modified by response
var res = self.device.invoke_request(msg.session, ctx.command_fields, ctx)
matter.profiler.log("COMMAND DONE")
var params_log = (ctx.log != nil) ? "(" + str(ctx.log) + ") " : ""
var cmd_log_level = ctx.endpoint != 0 ? 2 : 3 #- don't log for endpoint 0 -#
if tasmota.loglevel(cmd_log_level)
tasmota.log(format("MTR: >Command1 (%6i) %s %s %s", msg.session.local_session_id, ctx_str, cmd_name ? cmd_name : "", params_log), cmd_log_level)
end
# tasmota.log("MTR: Perf/Command = " + str(debug.counters()), 4)
ctx.log = nil
var raw = bytes(48)
# prefix 1528003601
raw.add(0x15280036, -4) # add 15280036
raw.add(0x01, 1) # add 01
if res == true || ctx.status == matter.SUCCESS # special case, just respond ok
ctx.status = matter.SUCCESS
self.invokeresponse2raw(raw, ctx, nil)
if tasmota.loglevel(3)
tasmota.log(f"MTR: <Replied ({msg.session.local_session_id:6i}) OK exch={msg.exchange_id:i}", 3)
end
elif res != nil
self.invokeresponse2raw(raw, ctx, res)
if !cmd_name cmd_name = "" end
if tasmota.loglevel(3)
tasmota.log(f"MTR: <Replied ({msg.session.local_session_id:6i}) {ctx} {cmd_name}", 3)
end
elif ctx.status != nil
self.invokeresponse2raw(raw, ctx, nil)
if tasmota.loglevel(3)
tasmota.log(f"MTR: <Replied ({msg.session.local_session_id:6i}) Status=0x{ctx.status:02X} exch={msg.exchange_id:i}", 3)
end
else
if tasmota.loglevel(3)
tasmota.log(f"MTR: _Ignore ({msg.session.local_session_id:6i}) exch={msg.exchange_id:i}", 3)
end
# ignore if content is nil and status is undefined
return false
end
# add suffix 1824FF0118
raw.add(0x1824FF01, -4) # add 1824FF01
raw.add(0x18, 1) # add 18
# tasmota.log(f"MTR: raw={raw.tohex()}", 3)
var resp = msg.build_response(0x09 #-Invoke Response-#, true)
var responder = self.device.message_handler
var msg_raw = msg.raw
msg_raw.clear()
resp.encode_frame(raw, msg_raw) # payload in cleartext
resp.encrypt()
responder.send_response_frame(resp)
matter.profiler.log("RESPONSE SENT")
return true
end
############################################################# #############################################################
# process IM 0x04 Subscribe Response # process IM 0x04 Subscribe Response
# #
@ -523,6 +1053,7 @@ class Matter_IM
# structure is `ReadRequestMessage` 10.6.2 p.558 # structure is `ReadRequestMessage` 10.6.2 p.558
# tasmota.log("MTR: IM:write_request processing start", 4) # tasmota.log("MTR: IM:write_request processing start", 4)
var ctx = matter.Path() var ctx = matter.Path()
ctx.msg = msg
if query.write_requests != nil if query.write_requests != nil
# prepare the response # prepare the response
@ -626,7 +1157,7 @@ class Matter_IM
tasmota.log(format("MTR: <Sub_Data (%6i) sub=%i", session.local_session_id, sub.subscription_id), 3) tasmota.log(format("MTR: <Sub_Data (%6i) sub=%i", session.local_session_id, sub.subscription_id), 3)
sub.is_keep_alive = false # sending an actual data update sub.is_keep_alive = false # sending an actual data update
var ret = self._inner_process_read_request(session, fake_read) var ret = self._inner_process_read_request(session, fake_read, nil #-no msg-#)
ret.suppress_response = false ret.suppress_response = false
ret.subscription_id = sub.subscription_id ret.subscription_id = sub.subscription_id

View File

@ -40,6 +40,7 @@ import matter
#@ solidify:matter.StatusIB,weak #@ solidify:matter.StatusIB,weak
#@ solidify:matter.StatusResponseMessage,weak #@ solidify:matter.StatusResponseMessage,weak
#@ solidify:matter.ReadRequestMessage,weak #@ solidify:matter.ReadRequestMessage,weak
#@ solidify:matter.ReadRequestMessage_solo,weak
#@ solidify:matter.ReportDataMessage,weak #@ solidify:matter.ReportDataMessage,weak
#@ solidify:matter.SubscribeRequestMessage,weak #@ solidify:matter.SubscribeRequestMessage,weak
#@ solidify:matter.SubscribeResponseMessage,weak #@ solidify:matter.SubscribeResponseMessage,weak
@ -47,6 +48,7 @@ import matter
#@ solidify:matter.WriteResponseMessage,weak #@ solidify:matter.WriteResponseMessage,weak
#@ solidify:matter.TimedRequestMessage,weak #@ solidify:matter.TimedRequestMessage,weak
#@ solidify:matter.InvokeRequestMessage,weak #@ solidify:matter.InvokeRequestMessage,weak
#@ solidify:matter.InvokeRequestMessage_solo,weak
#@ solidify:matter.InvokeResponseMessage,weak #@ solidify:matter.InvokeResponseMessage,weak
################################################################################# #################################################################################
@ -627,6 +629,238 @@ class Matter_ReadRequestMessage : Matter_IM_Message_base
end end
matter.ReadRequestMessage = Matter_ReadRequestMessage matter.ReadRequestMessage = Matter_ReadRequestMessage
#################################################################################
# ReadRequestMessage class optimized for a simple solo argument
#################################################################################
class Matter_Path end # for compilation
class Matter_ReadRequestMessage_solo : Matter_Path
# var endpoint # int
# var cluster # int
# var attribute # int
# var fabric_filtered # bool or nil
def from_raw(raw, idx)
self.reset()
# must start with 15360017
var sz = size(raw)
var val
if raw.get(idx, -4) != 0x15360017 return nil end
idx += 4
while idx < sz
# expect 24 xx yy or 25 xx yyyy (little endian)
var tag = raw.get(idx+1, 1)
var b0 = raw.get(idx, 1)
if b0 == 0x24 # u1
val = raw.get(idx+2, 1)
idx += 3
elif b0 == 0x25 # u2
val = raw.get(idx+2, 2)
idx += 4
else
break
end
if tag == 2
self.endpoint = val
elif tag == 3
self.cluster = val
elif tag == 4
self.attribute = val
else
return nil # unsupported tag
end
end
# expect 18 18
val = raw.get(idx, -2)
if val != 0x1818 return nil end
idx += 2
# fabric_filtered: 2803 or 2903
val = raw.get(idx, -2)
if val == 0x2803
self.fabric_filtered = false
idx += 2
elif val == 0x2903
self.fabric_filtered = true
idx += 2
end
# skip 24FFxx
val = raw.get(idx, -2)
if val == 0x24FF idx += 3 end
# expect 18
if raw.get(idx, 1) != 0x18 return nil end
idx += 1
# sanity check
if self.endpoint == nil ||
self.cluster == nil ||
self.attribute == nil ||
self.fabric_filtered == nil
return nil
end
# all good
return self
end
# Example: read_attr Reachable
# 153600172402012403392404111818290324FF0118
# 15 structure
# 3600 tag 00 array
# 17 list
# 2402 tag 02 u1 "endpoint"
# 01
# 2403 tag 03 u1 "cluster"
# 39 57U
# 2404 tag 04 u1 "attribute"
# 11 17U
# 18
# 18
# 2903 tag 03 booltrue
# 24FF tag FF u1
# 01 1U
# 18
# {0 = [[[2 = 1U, 3 = 57U, 4 = 17U]]], 3 = true, 255 = 1U}
end
matter.ReadRequestMessage_solo = Matter_ReadRequestMessage_solo
# test
# b = bytes('0000153600172402012403392404111818290324FF0118')
# m = matter.ReadRequestMessage_solo()
# mm = m.from_raw(b, 2)
# print(m)
#################################################################################
# InvokeRequestMessage class optimized for a simple solo argument
#################################################################################
class Matter_Path end # for compilation
class Matter_InvokeRequestMessage_solo : Matter_Path
var SuppressResponse
var TimedRequest
var command_fields
# var endpoint # int
# var cluster # int
# var attribute # int
# var fabric_filtered # bool or nil
def reset()
var n = nil
super(self).reset()
self.SuppressResponse = n
self.TimedRequest = n
self.command_fields = n
end
def from_raw(raw, idx)
self.reset()
# must start with 15360017
var sz = size(raw)
var val
if raw.get(idx, 1) != 0x15 return nil end
idx += 1
# check SuppressResponse (optional)
val = raw.get(idx, -2)
if val == 0x2800 || val == 0x2900
self.SuppressResponse = (val == 0x2900)
idx += 2
end
# check TimedRequest (optional)
val = raw.get(idx, -2)
if val == 0x2801 || val == 0x2901
self.SuppressResponse = (val == 0x2901)
idx += 2
end
# start of CommandDataIB
if raw.get(idx, -2) != 0x3602 return nil end
idx += 2
if raw.get(idx, 1) != 0x15 return nil end
idx += 1
if raw.get(idx, -2) != 0x3700 return nil end
idx += 2
#
while idx < sz
# expect 24 xx yy or 25 xx yyyy (little endian)
var tag = raw.get(idx+1, 1)
var b0 = raw.get(idx, 1)
if b0 == 0x24 # u1
val = raw.get(idx+2, 1)
idx += 3
elif b0 == 0x25 # u2
val = raw.get(idx+2, 2)
idx += 4
else
break
end
if tag == 0
self.endpoint = val
elif tag == 1
self.cluster = val
elif tag == 2
self.command = val
else
return nil # unsupported tag
end
end
if raw.get(idx, 1) != 0x18 return nil end
idx += 1
# command_fields
if raw.get(idx, -2) != 0x3501 return nil end
self.command_fields = matter.TLV.parse(raw, idx)
idx = self.command_fields.next_idx # skip structure
# close
if raw.get(idx, -2) != 0x1818 return nil end
idx += 2
if raw.get(idx, -4) != 0x24FF0118 return nil end
# all good
return self
end
# Example: command OnOff
# {0 = false, 1 = false, 2 = [{0 = [[0 = 1U, 1 = 6U, 2 = 0U]], 1 = {}}], 255 = 1U}
# 1528002801360215370024000124010624020018350118181824FF0118
#
# 15
# 2800 0 = false SuppressResponse
# 2801 1 = false TimedRequest
# 3602 2 = list of CommandDataIB
# 15
# 3700 0 = CommandPathIB
# 240001 0 = 1U endpoint
# 240106 1 = 6U cluster
# 240200 2 = 0U command
# 18
# 3501 1 = struct
# 18
# 18
# 18
# 24FF01 FF = 1U
# 18
# 15 structure
# 3600 tag 00 array
# 17 list
# 2402 tag 02 u1 "endpoint"
# 01
# 2403 tag 03 u1 "cluster"
# 39 57U
# 2404 tag 04 u1 "attribute"
# 11 17U
# 18
# 18
# 2903 tag 03 booltrue
# 24FF tag FF u1
# 01 1U
# 18
# {0 = [[[2 = 1U, 3 = 57U, 4 = 17U]]], 3 = true, 255 = 1U}
end
matter.InvokeRequestMessage_solo = Matter_InvokeRequestMessage_solo
################################################################################# #################################################################################
# ReportDataMessage class # ReportDataMessage class
################################################################################# #################################################################################

View File

@ -44,11 +44,16 @@ class Matter_IM_Message
# build a response message stub # build a response message stub
def init(msg, opcode, reliable) def init(msg, opcode, reliable)
self.reset(msg, opcode, reliable)
end
def reset(msg, opcode, reliable)
self.resp = msg.build_response(opcode, reliable) self.resp = msg.build_response(opcode, reliable)
self.ready = true # by default send immediately self.ready = true # by default send immediately
self.expiration = tasmota.millis() + self.MSG_TIMEOUT self.expiration = tasmota.millis() + self.MSG_TIMEOUT
self.last_counter = 0 # avoid `nil` value self.last_counter = 0 # avoid `nil` value
self.finish = false self.finish = false
self.data = nil
end end
# the message is being removed due to expiration # the message is being removed due to expiration
@ -88,10 +93,18 @@ class Matter_IM_Message
def send_im(responder) def send_im(responder)
# tasmota.log(format("MTR: IM_Message send_im exch=%i ready=%i", self.resp.exchange_id, self.ready ? 1 : 0), 3) # tasmota.log(format("MTR: IM_Message send_im exch=%i ready=%i", self.resp.exchange_id, self.ready ? 1 : 0), 3)
if !self.ready return false end if !self.ready return false end
# import debug
var resp = self.resp var resp = self.resp
resp.encode_frame(self.data.to_TLV().tlv2raw()) # payload in cleartext var data_tlv = self.data.to_TLV()
# matter.profiler.log(str(data_tlv))
var data_raw = data_tlv.tlv2raw() # payload in cleartext
# matter.profiler.log(data_raw.tohex())
resp.encode_frame(data_raw) # payload in cleartext
resp.encrypt() resp.encrypt()
tasmota.log(format("MTR: <snd (%6i) id=%i exch=%i rack=%s", resp.session.local_session_id, resp.message_counter, resp.exchange_id, resp.ack_message_counter), 4) if tasmota.loglevel(4)
tasmota.log(format("MTR: <snd (%6i) id=%i exch=%i rack=%s", resp.session.local_session_id, resp.message_counter, resp.exchange_id, resp.ack_message_counter), 4)
end
# tasmota.log("MTR: Perf/Send = " + str(debug.counters()), 4)
responder.send_response_frame(resp) responder.send_response_frame(resp)
self.last_counter = resp.message_counter self.last_counter = resp.message_counter
self.finish = true # by default we remove the packet after it is sent self.finish = true # by default we remove the packet after it is sent
@ -295,7 +308,9 @@ class Matter_IM_ReportDataSubscribed : Matter_IM_ReportData
var resp = self.resp.build_standalone_ack(false) var resp = self.resp.build_standalone_ack(false)
resp.encode_frame() resp.encode_frame()
resp.encrypt() resp.encrypt()
tasmota.log(format("MTR: <Ack (%6i) ack=%i id=%i", resp.session.local_session_id, resp.ack_message_counter, resp.message_counter), 4) if tasmota.loglevel(4)
tasmota.log(format("MTR: <Ack (%6i) ack=%i id=%i", resp.session.local_session_id, resp.ack_message_counter, resp.message_counter), 4)
end
responder.send_response_frame(resp) responder.send_response_frame(resp)
self.last_counter = resp.message_counter self.last_counter = resp.message_counter
self.finish = true self.finish = true
@ -420,7 +435,9 @@ class Matter_IM_SubscribeResponse : Matter_IM_ReportData
def status_ok_received(msg) def status_ok_received(msg)
# tasmota.log(format("MTR: IM_SubscribeResponse status_ok_received sub=%i exch=%i ack=%i last_counter=%i", self.sub.subscription_id, self.resp.exchange_id, msg.ack_message_counter ? msg.ack_message_counter : 0 , self.last_counter), 3) # tasmota.log(format("MTR: IM_SubscribeResponse status_ok_received sub=%i exch=%i ack=%i last_counter=%i", self.sub.subscription_id, self.resp.exchange_id, msg.ack_message_counter ? msg.ack_message_counter : 0 , self.last_counter), 3)
# once we receive ack, open flow for subscriptions # once we receive ack, open flow for subscriptions
tasmota.log(format("MTR: >Sub_OK (%6i) sub=%i", msg.session.local_session_id, self.sub.subscription_id), 3) if tasmota.loglevel(3)
tasmota.log(format("MTR: >Sub_OK (%6i) sub=%i", msg.session.local_session_id, self.sub.subscription_id), 3)
end
return super(self).status_ok_received(msg) return super(self).status_ok_received(msg)
end end

View File

@ -171,8 +171,12 @@ class Matter_Frame
# #
# Header is built from attributes # Header is built from attributes
# `payload` is a bytes() buffer for the app payload # `payload` is a bytes() buffer for the app payload
def encode_frame(payload) #
var raw = bytes() # you can pass a `raw` bytes() object to be used
def encode_frame(payload, raw)
if raw == nil
raw = bytes(16 + (payload ? size(payload) : 0))
end
# compute flags # compute flags
if self.flags == nil if self.flags == nil
self.flags = 0x00 self.flags = 0x00
@ -220,7 +224,7 @@ class Matter_Frame
raw .. payload raw .. payload
end end
self.debug(raw) # self.debug(raw)
self.raw = raw self.raw = raw
return raw return raw
end end
@ -343,11 +347,15 @@ class Matter_Frame
############################################################# #############################################################
# decrypt with I2S key # decrypt with I2S key
# return cleartext or `nil` if failed # return cleartext or `nil` if failed
#
# frame.raw is decrypted in-place and the MIC is removed
# returns true if successful
def decrypt() def decrypt()
import crypto import crypto
var session = self.session var session = self.session
var raw = self.raw var raw = self.raw
var mic = raw[-16..] # take last 16 bytes as signature var payload_idx = self.payload_idx
var tag_len = 16
# decrypt the message with `i2r` key # decrypt the message with `i2r` key
var i2r = session.get_i2r() var i2r = session.get_i2r()
@ -357,6 +365,7 @@ class Matter_Frame
# compute privacy key, p.71 # compute privacy key, p.71
tasmota.log("MTR: >>>>>>>>>>>>>>>>>>>> Compute Privacy TODO", 2) tasmota.log("MTR: >>>>>>>>>>>>>>>>>>>> Compute Privacy TODO", 2)
var k = session.get_i2r_privacy() var k = session.get_i2r_privacy()
var mic = raw[-16..] # take last 16 bytes as signature
var n = bytes().add(self.local_session_id, -2) + mic[5..15] # session in Big Endian var n = bytes().add(self.local_session_id, -2) + mic[5..15] # session in Big Endian
var m = self.raw[4 .. self.payload_idx-1] var m = self.raw[4 .. self.payload_idx-1]
var m_clear = crypto.AES_CTR(k).decrypt(m, n, 2) var m_clear = crypto.AES_CTR(k).decrypt(m, n, 2)
@ -364,11 +373,9 @@ class Matter_Frame
self.raw = self.raw[0..3] + m_clear + m[self.payload_idx .. ] self.raw = self.raw[0..3] + m_clear + m[self.payload_idx .. ]
end end
# use AES_CCM
var a = raw[0 .. self.payload_idx - 1]
var p = raw[self.payload_idx .. -17]
# recompute nonce # recompute nonce
var n = bytes() var n = self.message_handler._n_bytes # use cached bytes() object to avoid allocation
n.clear()
n.add(self.flags, 1) n.add(self.flags, 1)
n.add(self.message_counter, 4) n.add(self.message_counter, 4)
if self.source_node_id if self.source_node_id
@ -381,28 +388,26 @@ class Matter_Frame
end end
# tasmota.log("MTR: ******************************", 4) # tasmota.log("MTR: ******************************", 4)
# tasmota.log("MTR: raw =" + raw.tohex(), 4)
# tasmota.log("MTR: i2r =" + i2r.tohex(), 4) # tasmota.log("MTR: i2r =" + i2r.tohex(), 4)
# tasmota.log("MTR: p =" + p.tohex(), 4) # tasmota.log("MTR: p =" + raw[payload_idx .. -17].tohex(), 4)
# tasmota.log("MTR: a =" + a.tohex(), 4) # tasmota.log("MTR: a =" + raw[0 .. payload_idx - 1].tohex(), 4)
# tasmota.log("MTR: n =" + n.tohex(), 4) # tasmota.log("MTR: n =" + n.tohex(), 4)
# tasmota.log("MTR: mic =" + mic.tohex(), 4) # tasmota.log("MTR: mic =" + raw[-16..].tohex(), 4)
# decrypt # decrypt
var aes = crypto.AES_CCM(i2r, n, a, size(p), 16) var ret = crypto.AES_CCM.decrypt1(i2r, # secret key
var cleartext = aes.decrypt(p) n, 0, size(n), # nonce / IV
var tag = aes.tag() raw, 0, payload_idx, # aad
raw, payload_idx, size(raw) - payload_idx - tag_len, # encrypted - decrypted in-place
# tasmota.log("MTR: ******************************", 4) raw, size(raw) - tag_len, tag_len) # MIC
# tasmota.log("MTR: cleartext =" + cleartext.tohex(), 4) if ret
# tasmota.log("MTR: tag =" + tag.tohex(), 4) # succcess
# tasmota.log("MTR: ******************************", 4) raw.resize(size(raw) - tag_len) # remove MIC
else
if tag != mic
tasmota.log("MTR: rejected packet due to invalid MIC", 3) tasmota.log("MTR: rejected packet due to invalid MIC", 3)
return nil
end end
return ret
return cleartext
end end
############################################################# #############################################################
@ -413,15 +418,15 @@ class Matter_Frame
import crypto import crypto
var raw = self.raw var raw = self.raw
var session = self.session var session = self.session
var payload_idx = self.payload_idx
var tag_len = 16
# encrypt the message with `i2r` key # encrypt the message with `i2r` key
var r2i = session.get_r2i() var r2i = session.get_r2i()
# use AES_CCM
var a = raw[0 .. self.payload_idx - 1]
var p = raw[self.payload_idx .. ]
# recompute nonce # recompute nonce
var n = bytes() var n = self.message_handler._n_bytes # use cached bytes() object to avoid allocation
n.clear()
n.add(self.flags, 1) n.add(self.flags, 1)
n.add(self.message_counter, 4) n.add(self.message_counter, 4)
if session.is_CASE() && session.get_device_id() if session.is_CASE() && session.get_device_id()
@ -429,30 +434,14 @@ class Matter_Frame
end end
n.resize(13) # add zeros n.resize(13) # add zeros
# tasmota.log("MTR: cleartext: " + self.raw.tohex(), 4) # encrypt
raw.resize(size(raw) + tag_len) # make room for MIC
var ret = crypto.AES_CCM.encrypt1(r2i, # secret key
n, 0, size(n), # nonce / IV
raw, 0, payload_idx, # aad
raw, payload_idx, size(raw) - payload_idx - tag_len, # encrypted - decrypted in-place
raw, size(raw) - tag_len, tag_len) # MIC
# tasmota.log("MTR: ******************************", 4)
# tasmota.log("MTR: r2i =" + r2i.tohex(), 4)
# tasmota.log("MTR: p =" + p.tohex(), 4)
# tasmota.log("MTR: a =" + a.tohex(), 4)
# tasmota.log("MTR: n =" + n.tohex(), 4)
# decrypt
var aes = crypto.AES_CCM(r2i, n, a, size(p), 16)
var ciphertext = aes.encrypt(p)
var tag = aes.tag()
# tasmota.log("MTR: ******************************", 4)
# tasmota.log("MTR: ciphertext =" + ciphertext.tohex(), 4)
# tasmota.log("MTR: tag =" + tag.tohex(), 4)
# tasmota.log("MTR: ******************************", 4)
# packet is good, put back content in raw
self.raw.resize(self.payload_idx) # remove cleartext payload
self.raw .. ciphertext # add ciphertext
self.raw .. tag # add MIC
# tasmota.log("MTR: encrypted: " + self.raw.tohex(), 4)
end end
############################################################# #############################################################

View File

@ -30,12 +30,16 @@ class Matter_MessageHandler
var im # Instance of `matter.IM` handling Interaction Model var im # Instance of `matter.IM` handling Interaction Model
var control_message # Instance of `matter.Control_Message` for MCSP var control_message # Instance of `matter.Control_Message` for MCSP
# cache for decryption bytes
var _n_bytes # size 16 minimal, used by frame_buffer for decryption
############################################################# #############################################################
def init(device) def init(device)
self.device = device self.device = device
self.commissioning = matter.Commisioning_Context(self) self.commissioning = matter.Commisioning_Context(self)
self.im = matter.IM(device) self.im = matter.IM(device)
self.control_message = matter.Control_Message(self) self.control_message = matter.Control_Message(self)
self._n_bytes = bytes(16)
end end
############################################################# #############################################################
@ -47,7 +51,9 @@ class Matter_MessageHandler
if frame.x_flag_r # nothing to respond, check if we need a standalone ack if frame.x_flag_r # nothing to respond, check if we need a standalone ack
var resp = frame.build_standalone_ack(reliable) var resp = frame.build_standalone_ack(reliable)
resp.encode_frame() resp.encode_frame()
tasmota.log(format("MTR: <Ack (%6i) ack=%i id=%i %s", resp.session.local_session_id, resp.ack_message_counter, resp.message_counter, reliable ? '{reliable}' : ''), 4) if tasmota.loglevel(4)
tasmota.log(format("MTR: <Ack (%6i) ack=%i id=%i %s", resp.session.local_session_id, resp.ack_message_counter, resp.message_counter, reliable ? '{reliable}' : ''), 4)
end
self.send_response_frame(resp) self.send_response_frame(resp)
end end
end end
@ -62,7 +68,9 @@ class Matter_MessageHandler
var resp = frame.build_standalone_ack(reliable) var resp = frame.build_standalone_ack(reliable)
resp.encode_frame() resp.encode_frame()
resp.encrypt() resp.encrypt()
tasmota.log(format("MTR: <Ack* (%6i) ack=%i id=%i %s", resp.session.local_session_id, resp.ack_message_counter, resp.message_counter, reliable ? '{reliable}' : ''), 4) if tasmota.loglevel(4)
tasmota.log(format("MTR: <Ack* (%6i) ack=%i id=%i %s", resp.session.local_session_id, resp.ack_message_counter, resp.message_counter, reliable ? '{reliable}' : ''), 4)
end
self.send_response_frame(resp) self.send_response_frame(resp)
end end
end end
@ -76,11 +84,13 @@ class Matter_MessageHandler
def msg_received(raw, addr, port) def msg_received(raw, addr, port)
var ret = false var ret = false
matter.profiler.log("msg_received")
try try
# tasmota.log("MTR: MessageHandler::msg_received raw="+raw.tohex(), 4) # tasmota.log("MTR: MessageHandler::msg_received raw="+raw.tohex(), 4)
var frame = matter.Frame(self, raw, addr, port) var frame = matter.Frame(self, raw, addr, port)
var ok = frame.decode_header() var ok = frame.decode_header()
# matter.profiler.log("msg_received_header_decoded")
if !ok return false end if !ok return false end
# do we need decryption? # do we need decryption?
@ -102,7 +112,9 @@ class Matter_MessageHandler
# check if it's a duplicate # check if it's a duplicate
if !session._counter_insecure_rcv.validate(frame.message_counter, false) if !session._counter_insecure_rcv.validate(frame.message_counter, false)
tasmota.log(format("MTR: . Duplicate unencrypted message = %i ref = %i", frame.message_counter, session._counter_insecure_rcv.val()), 4) if tasmota.loglevel(4)
tasmota.log(format("MTR: . Duplicate unencrypted message = %i ref = %i", frame.message_counter, session._counter_insecure_rcv.val()), 4)
end
self.send_simple_ack(frame, false #-not reliable-#) self.send_simple_ack(frame, false #-not reliable-#)
return false return false
end end
@ -112,9 +124,13 @@ class Matter_MessageHandler
if frame.opcode != 0x10 # don't show `MRP_Standalone_Acknowledgement` if frame.opcode != 0x10 # don't show `MRP_Standalone_Acknowledgement`
var op_name = matter.get_opcode_name(frame.opcode) var op_name = matter.get_opcode_name(frame.opcode)
if !op_name op_name = format("0x%02X", frame.opcode) end if !op_name op_name = format("0x%02X", frame.opcode) end
tasmota.log(format("MTR: >Received (%6i) %s rid=%i exch=%i from [%s]:%i", session.local_session_id, op_name, frame.message_counter, frame.exchange_id, addr, port), 3) if tasmota.loglevel(3)
tasmota.log(format("MTR: >Received (%6i) %s rid=%i exch=%i from [%s]:%i", session.local_session_id, op_name, frame.message_counter, frame.exchange_id, addr, port), 3)
end
else else
tasmota.log(format("MTR: >rcv Ack (%6i) rid=%i exch=%i ack=%s %sfrom [%s]:%i", session.local_session_id, frame.message_counter, frame.x_flag_r ? "{reliable} " : "", frame.exchange_id, str(frame.ack_message_counter), addr, port), 4) if tasmota.loglevel(4)
tasmota.log(format("MTR: >rcv Ack (%6i) rid=%i exch=%i ack=%s %sfrom [%s]:%i", session.local_session_id, frame.message_counter, frame.x_flag_r ? "{reliable} " : "", frame.exchange_id, str(frame.ack_message_counter), addr, port), 4)
end
end end
ret = self.commissioning.process_incoming(frame) ret = self.commissioning.process_incoming(frame)
# if ret is false, the implicit Ack was not sent # if ret is false, the implicit Ack was not sent
@ -123,14 +139,19 @@ class Matter_MessageHandler
else else
############################################################# #############################################################
# encrypted message # encrypted message
tasmota.log(format("MTR: decode header: local_session_id=%i message_counter=%i", frame.local_session_id, frame.message_counter), 4) # matter.profiler.log("msg_received_header_encrypted_message_received")
if tasmota.loglevel(4)
tasmota.log(format("MTR: decode header: local_session_id=%i message_counter=%i", frame.local_session_id, frame.message_counter), 4)
end
var session = self.device.sessions.get_session_by_local_session_id(frame.local_session_id) var session = self.device.sessions.get_session_by_local_session_id(frame.local_session_id)
# matter.profiler.log("msg_received_header_session_retrieved")
if session == nil if session == nil
tasmota.log("MTR: unknown local_session_id="+str(frame.local_session_id), 3) tasmota.log("MTR: unknown local_session_id="+str(frame.local_session_id), 3)
# tasmota.log("MTR: frame="+matter.inspect(frame), 3) # tasmota.log("MTR: frame="+matter.inspect(frame), 3)
return false return false
end end
# matter.profiler.log("msg_received_session_found")
if addr session._ip = addr end if addr session._ip = addr end
if port session._port = port end if port session._port = port end
session._message_handler = self session._message_handler = self
@ -138,22 +159,26 @@ class Matter_MessageHandler
# check if it's a duplicate # check if it's a duplicate
if !session.counter_rcv_validate(frame.message_counter, true) if !session.counter_rcv_validate(frame.message_counter, true)
tasmota.log("MTR: . Duplicate encrypted message = " + str(frame.message_counter) + " counter=" + str(session.counter_rcv), 4) if tasmota.loglevel(3)
tasmota.log("MTR: . Duplicate encrypted message = " + str(frame.message_counter) + " counter=" + str(session.counter_rcv), 3)
end
self.send_encrypted_ack(frame, false #-not reliable-#) self.send_encrypted_ack(frame, false #-not reliable-#)
return false return false
end end
var cleartext = frame.decrypt() var decrypt_ok = frame.decrypt()
if !cleartext return false end matter.profiler.log("msg_received_header_frame_decrypted")
if !decrypt_ok return false end
# packet is good, put back content in raw # matter.profiler.log("msg_received_payload_undecoded")
frame.raw = frame.raw[0 .. frame.payload_idx - 1] # remove encrypted payload
frame.raw .. cleartext # add cleartext
# continue decoding # continue decoding
# tasmota.log(format("MTR: idx=%i clear=%s", frame.payload_idx, frame.raw.tohex()), 4) # tasmota.log(format("MTR: idx=%i clear=%s", frame.payload_idx, frame.raw.tohex()), 4)
frame.decode_payload() frame.decode_payload()
tasmota.log("MTR: > Decrypted message: protocol_id:"+str(frame.protocol_id)+" opcode="+str(frame.opcode)+" exchange_id="+str(frame.exchange_id & 0xFFFF), 4) # matter.profiler.log("msg_received_payload_decoded")
if tasmota.loglevel(4)
tasmota.log("MTR: > Decrypted message: protocol_id:"+str(frame.protocol_id)+" opcode="+str(frame.opcode)+" exchange_id="+str(frame.exchange_id & 0xFFFF), 4)
end
# tasmota.log(format("MTR: >rcv (%6i) [%02X/%02X] rid=%i exch=%i ack=%s %sfrom [%s]:%i", session.local_session_id, frame.protocol_id, frame.opcode, frame.message_counter, frame.exchange_id, str(frame.ack_message_counter), frame.x_flag_r ? "{reliable} " : "", addr, port), 3) # tasmota.log(format("MTR: >rcv (%6i) [%02X/%02X] rid=%i exch=%i ack=%s %sfrom [%s]:%i", session.local_session_id, frame.protocol_id, frame.opcode, frame.message_counter, frame.exchange_id, str(frame.ack_message_counter), frame.x_flag_r ? "{reliable} " : "", addr, port), 3)
@ -173,7 +198,9 @@ class Matter_MessageHandler
ret = true ret = true
elif protocol_id == 0x0001 # PROTOCOL_ID_INTERACTION_MODEL elif protocol_id == 0x0001 # PROTOCOL_ID_INTERACTION_MODEL
# dispatch to IM Protocol Messages # dispatch to IM Protocol Messages
matter.profiler.log("process_IM_start")
ret = self.im.process_incoming(frame) ret = self.im.process_incoming(frame)
matter.profiler.log("process_IM_end")
# if `ret` is true, we have something to send # if `ret` is true, we have something to send
if ret if ret
self.im.send_enqueued(self) self.im.send_enqueued(self)
@ -220,6 +247,7 @@ class Matter_MessageHandler
# msg.exchange_id: exchange id (int) # msg.exchange_id: exchange id (int)
# msg.local_session_id: local session (for logging) # msg.local_session_id: local session (for logging)
def send_response_frame(msg) def send_response_frame(msg)
matter.profiler.log("send_response_frame")
self.device.msg_send(msg) self.device.msg_send(msg)
end end

View File

@ -30,9 +30,24 @@ class Matter_Path
var endpoint # endpoint or `nil` if expansion var endpoint # endpoint or `nil` if expansion
var cluster # cluster or `nil` if expansion var cluster # cluster or `nil` if expansion
var attribute # attribute or `nil` if expansion var attribute # attribute or `nil` if expansion
var fabric_filtered # bool or nil
var command # command var command # command
var status # status to be returned (matter.SUCCESS or matter.<ERROR>) var status # status to be returned (matter.SUCCESS or matter.<ERROR>)
var log # any string that needs to be logged (used to show significant parameters for commands) var log # any string that needs to be logged (used to show significant parameters for commands)
var msg # reference of the original message
# reset the object, allows reuse of the same object
def reset()
var n = nil # it's actually more compact code to load `nil` into a register and assign all members
self.endpoint = n
self.cluster = n
self.attribute = n
self.fabric_filtered = n
self.command = n
self.status = n
self.log = n
self.msg = n
end
def tostring() def tostring()
try try
@ -41,6 +56,7 @@ class Matter_Path
s += (self.cluster != nil ? format("%04X/", self.cluster) : "****/") s += (self.cluster != nil ? format("%04X/", self.cluster) : "****/")
s += (self.attribute != nil ? format("%04X", self.attribute) : "") s += (self.attribute != nil ? format("%04X", self.attribute) : "")
s += (self.command != nil ? format("%04X", self.command) : "") s += (self.command != nil ? format("%04X", self.command) : "")
if self.fabric_filtered s += "!" end
if self.attribute == nil && self.command == nil s += "****" end if self.attribute == nil && self.command == nil s += "****" end
return s return s
except .. as e, m except .. as e, m

View File

@ -63,6 +63,15 @@ class Matter_Plugin
self.parse_configuration(config) self.parse_configuration(config)
end end
# proxy for the same method in IM
def ack_request(ctx)
var msg = ctx.msg
if msg != nil
self.device.message_handler.im.send_ack_now(msg)
end
ctx.msg = nil
end
############################################################# #############################################################
# parse_configuration # parse_configuration
# #
@ -142,16 +151,32 @@ class Matter_Plugin
def get_endpoint() def get_endpoint()
return self.endpoint return self.endpoint
end end
def get_cluster_list(ep) def get_cluster_list()
var ret = [] var ret = []
for k: self.clusters.keys() for k: self.clusters.keys()
ret.push(k) ret.push(k)
end end
return ret return ret
end end
def get_attribute_list(ep, cluster) def contains_cluster(cluster)
return self.clusters.contains(cluster)
end
def get_attribute_list(cluster)
return self.clusters.find(cluster, []) return self.clusters.find(cluster, [])
end end
def contains_attribute(cluster, attribute)
var attr_list = self.clusters.find(cluster)
if attr_list != nil
var idx = 0
while idx < size(attr_list)
if attr_list[idx] == attribute
return true
end
idx += 1
end
end
return false
end
############################################################# #############################################################
# Does it handle this endpoint and this cluster # Does it handle this endpoint and this cluster
@ -166,7 +191,10 @@ class Matter_Plugin
############################################################# #############################################################
############################################################# #############################################################
# read attribute # read attribute
def read_attribute(session, ctx) #
# Arg:
# `tlv_solo` contains an instance of `Matter_TLV_item` to avoid allocating a new object with TLV.create_TLV
def read_attribute(session, ctx, tlv_solo)
var TLV = matter.TLV var TLV = matter.TLV
var cluster = ctx.cluster var cluster = ctx.cluster
var attribute = ctx.attribute var attribute = ctx.attribute
@ -195,18 +223,18 @@ class Matter_Plugin
var pl = TLV.Matter_TLV_array() var pl = TLV.Matter_TLV_array()
return pl return pl
elif attribute == 0xFFFC # ---------- FeatureMap / map32 ---------- elif attribute == 0xFFFC # ---------- FeatureMap / map32 ----------
return TLV.create_TLV(TLV.U4, 0) # return tlv_solo.set(TLV.U4, 0) #
elif attribute == 0xFFFD # ---------- ClusterRevision / u2 ---------- elif attribute == 0xFFFD # ---------- ClusterRevision / u2 ----------
return TLV.create_TLV(TLV.U4, 1) # "Initial Release" return tlv_solo.set(TLV.U4, 1) # "Initial Release"
end end
# ==================================================================================================== # ====================================================================================================
elif cluster == 0x0039 # ========== Bridged Device Basic Information 9.13 p.485 ========== elif cluster == 0x0039 # ========== Bridged Device Basic Information 9.13 p.485 ==========
if attribute == 0x0011 # ---------- Reachable / bool ---------- if attribute == 0x0011 # ---------- Reachable / bool ----------
return TLV.create_TLV(TLV.BOOL, 1) # by default we are reachable return tlv_solo.set(TLV.BOOL, 1) # by default we are reachable
else else
return super(self).read_attribute(session, ctx) # rest is handled by 0x0028 return super(self).read_attribute(session, ctx, tlv_solo) # rest is handled by 0x0028
end end
else else
return nil return nil

View File

@ -37,7 +37,7 @@ class Matter_Plugin_Aggregator : Matter_Plugin
############################################################# #############################################################
# read an attribute # read an attribute
# #
def read_attribute(session, ctx) def read_attribute(session, ctx, tlv_solo)
var TLV = matter.TLV var TLV = matter.TLV
var cluster = ctx.cluster var cluster = ctx.cluster
var attribute = ctx.attribute var attribute = ctx.attribute
@ -55,11 +55,11 @@ class Matter_Plugin_Aggregator : Matter_Plugin
end end
return pl return pl
else else
return super(self).read_attribute(session, ctx) return super(self).read_attribute(session, ctx, tlv_solo)
end end
else else
return super(self).read_attribute(session, ctx) return super(self).read_attribute(session, ctx, tlv_solo)
end end
# no match found, return that the attribute is unsupported # no match found, return that the attribute is unsupported

View File

@ -175,7 +175,7 @@ class Matter_Plugin_Bridge_HTTP : Matter_Plugin_Device
############################################################# #############################################################
# read attribute # read attribute
# #
def read_attribute(session, ctx) def read_attribute(session, ctx, tlv_solo)
var TLV = matter.TLV var TLV = matter.TLV
var cluster = ctx.cluster var cluster = ctx.cluster
var attribute = ctx.attribute var attribute = ctx.attribute
@ -187,35 +187,35 @@ class Matter_Plugin_Bridge_HTTP : Matter_Plugin_Device
if attribute == 0x0003 # ---------- ProductName / string ---------- if attribute == 0x0003 # ---------- ProductName / string ----------
var name = self.http_remote.get_info().find("name") var name = self.http_remote.get_info().find("name")
if name if name
return TLV.create_TLV(TLV.UTF1, name) return tlv_solo.set(TLV.UTF1, name)
else else
return TLV.create_TLV(TLV.NULL, nil) return tlv_solo.set(TLV.NULL, nil)
end end
elif attribute == 0x000A # ---------- SoftwareVersionString / string ---------- elif attribute == 0x000A # ---------- SoftwareVersionString / string ----------
var version_full = self.http_remote.get_info().find("version") var version_full = self.http_remote.get_info().find("version")
if version_full if version_full
var version_end = string.find(version_full, '(') var version_end = string.find(version_full, '(')
if version_end > 0 version_full = version_full[0..version_end - 1] end if version_end > 0 version_full = version_full[0..version_end - 1] end
return TLV.create_TLV(TLV.UTF1, version_full) return tlv_solo.set(TLV.UTF1, version_full)
else else
return TLV.create_TLV(TLV.NULL, nil) return tlv_solo.set(TLV.NULL, nil)
end end
elif attribute == 0x000F || attribute == 0x0012 # ---------- SerialNumber || UniqueID / string ---------- elif attribute == 0x000F || attribute == 0x0012 # ---------- SerialNumber || UniqueID / string ----------
var mac = self.http_remote.get_info().find("mac") var mac = self.http_remote.get_info().find("mac")
if mac if mac
return TLV.create_TLV(TLV.UTF1, mac) return tlv_solo.set(TLV.UTF1, mac)
else else
return TLV.create_TLV(TLV.NULL, nil) return tlv_solo.set(TLV.NULL, nil)
end end
elif attribute == 0x0011 # ---------- Reachable / bool ---------- elif attribute == 0x0011 # ---------- Reachable / bool ----------
# self.is_reachable_lazy_sync() # Not needed anymore # self.is_reachable_lazy_sync() # Not needed anymore
return TLV.create_TLV(TLV.BOOL, self.http_remote.reachable) # TODO find a way to do a ping return tlv_solo.set(TLV.BOOL, self.http_remote.reachable) # TODO find a way to do a ping
else else
return super(self).read_attribute(session, ctx) return super(self).read_attribute(session, ctx, tlv_solo)
end end
else else
return super(self).read_attribute(session, ctx) return super(self).read_attribute(session, ctx, tlv_solo)
end end
end end

View File

@ -30,7 +30,7 @@ class Matter_Plugin_Bridge_Light0 : Matter_Plugin_Bridge_HTTP
static var TYPE = "http_light0" # name of the plug-in in json static var TYPE = "http_light0" # name of the plug-in in json
static var NAME = "Light 0 On" # display name of the plug-in static var NAME = "Light 0 On" # display name of the plug-in
static var ARG = "relay" # additional argument name (or empty if none) static var ARG = "relay" # additional argument name (or empty if none)
static var ARG_HINT = "Enter Power<x> number" static var ARG_HINT = "Power<x> number"
static var ARG_TYPE = / x -> int(x) # function to convert argument to the right type static var ARG_TYPE = / x -> int(x) # function to convert argument to the right type
# static var UPDATE_TIME = 3000 # update every 3s # static var UPDATE_TIME = 3000 # update every 3s
# static var UPDATE_CMD = "Status 11" # command to send for updates # static var UPDATE_CMD = "Status 11" # command to send for updates
@ -83,13 +83,14 @@ class Matter_Plugin_Bridge_Light0 : Matter_Plugin_Bridge_HTTP
var ret = self.call_remote_sync("Power" + str(self.tasmota_relay_index), v ? "1" : "0") var ret = self.call_remote_sync("Power" + str(self.tasmota_relay_index), v ? "1" : "0")
if ret != nil if ret != nil
self.parse_update(ret, 11) # update shadow from return value self.parse_update(ret, 11) # update shadow from return value
# self.tick = self.device.tick # prevent an explicit Status11 for as it is not needed if the subscription update is sent in same tick
end end
end end
############################################################# #############################################################
# read an attribute # read an attribute
# #
def read_attribute(session, ctx) def read_attribute(session, ctx, tlv_solo)
var TLV = matter.TLV var TLV = matter.TLV
var cluster = ctx.cluster var cluster = ctx.cluster
var attribute = ctx.attribute var attribute = ctx.attribute
@ -98,15 +99,15 @@ class Matter_Plugin_Bridge_Light0 : Matter_Plugin_Bridge_HTTP
if cluster == 0x0006 # ========== On/Off 1.5 p.48 ========== if cluster == 0x0006 # ========== On/Off 1.5 p.48 ==========
self.update_shadow_lazy() self.update_shadow_lazy()
if attribute == 0x0000 # ---------- OnOff / bool ---------- if attribute == 0x0000 # ---------- OnOff / bool ----------
return TLV.create_TLV(TLV.BOOL, self.shadow_onoff) return tlv_solo.set(TLV.BOOL, self.shadow_onoff)
elif attribute == 0xFFFC # ---------- FeatureMap / map32 ---------- elif attribute == 0xFFFC # ---------- FeatureMap / map32 ----------
return TLV.create_TLV(TLV.U4, 0) # 0 = no Level Control for Lighting return tlv_solo.set(TLV.U4, 0) # 0 = no Level Control for Lighting
elif attribute == 0xFFFD # ---------- ClusterRevision / u2 ---------- elif attribute == 0xFFFD # ---------- ClusterRevision / u2 ----------
return TLV.create_TLV(TLV.U4, 4) # 0 = no Level Control for Lighting return tlv_solo.set(TLV.U4, 4) # 0 = no Level Control for Lighting
end end
else else
return super(self).read_attribute(session, ctx) return super(self).read_attribute(session, ctx, tlv_solo)
end end
end end
@ -122,7 +123,6 @@ class Matter_Plugin_Bridge_Light0 : Matter_Plugin_Bridge_HTTP
# ==================================================================================================== # ====================================================================================================
if cluster == 0x0006 # ========== On/Off 1.5 p.48 ========== if cluster == 0x0006 # ========== On/Off 1.5 p.48 ==========
self.update_shadow_lazy()
if command == 0x0000 # ---------- Off ---------- if command == 0x0000 # ---------- Off ----------
self.set_onoff(false) self.set_onoff(false)
return true return true

View File

@ -78,7 +78,7 @@ class Matter_Plugin_Bridge_Light1 : Matter_Plugin_Bridge_Light0
############################################################# #############################################################
# read an attribute # read an attribute
# #
def read_attribute(session, ctx) def read_attribute(session, ctx, tlv_solo)
var TLV = matter.TLV var TLV = matter.TLV
var cluster = ctx.cluster var cluster = ctx.cluster
var attribute = ctx.attribute var attribute = ctx.attribute
@ -88,30 +88,30 @@ class Matter_Plugin_Bridge_Light1 : Matter_Plugin_Bridge_Light0
self.update_shadow_lazy() self.update_shadow_lazy()
if attribute == 0x0000 # ---------- CurrentLevel / u1 ---------- if attribute == 0x0000 # ---------- CurrentLevel / u1 ----------
if self.shadow_bri != nil if self.shadow_bri != nil
return TLV.create_TLV(TLV.U1, self.shadow_bri) return tlv_solo.set(TLV.U1, self.shadow_bri)
else else
return TLV.create_TLV(TLV.NULL, nil) return tlv_solo.set(TLV.NULL, nil)
end end
elif attribute == 0x0002 # ---------- MinLevel / u1 ---------- elif attribute == 0x0002 # ---------- MinLevel / u1 ----------
return TLV.create_TLV(TLV.U1, 0) return tlv_solo.set(TLV.U1, 0)
elif attribute == 0x0003 # ---------- MaxLevel / u1 ---------- elif attribute == 0x0003 # ---------- MaxLevel / u1 ----------
return TLV.create_TLV(TLV.U1, 254) return tlv_solo.set(TLV.U1, 254)
elif attribute == 0x000F # ---------- Options / map8 ---------- elif attribute == 0x000F # ---------- Options / map8 ----------
return TLV.create_TLV(TLV.U1, 0) # return tlv_solo.set(TLV.U1, 0) #
elif attribute == 0x0011 # ---------- OnLevel / u1 ---------- elif attribute == 0x0011 # ---------- OnLevel / u1 ----------
if self.shadow_bri != nil if self.shadow_bri != nil
return TLV.create_TLV(TLV.U1, self.shadow_bri) return tlv_solo.set(TLV.U1, self.shadow_bri)
else else
return TLV.create_TLV(TLV.NULL, nil) return tlv_solo.set(TLV.NULL, nil)
end end
elif attribute == 0xFFFC # ---------- FeatureMap / map32 ---------- elif attribute == 0xFFFC # ---------- FeatureMap / map32 ----------
return TLV.create_TLV(TLV.U4, 0X01) # OnOff return tlv_solo.set(TLV.U4, 0X01) # OnOff
elif attribute == 0xFFFD # ---------- ClusterRevision / u2 ---------- elif attribute == 0xFFFD # ---------- ClusterRevision / u2 ----------
return TLV.create_TLV(TLV.U4, 5) # "new data model format and notation" return tlv_solo.set(TLV.U4, 5) # "new data model format and notation"
end end
else else
return super(self).read_attribute(session, ctx) return super(self).read_attribute(session, ctx, tlv_solo)
end end
end end
@ -127,7 +127,6 @@ class Matter_Plugin_Bridge_Light1 : Matter_Plugin_Bridge_Light0
# ==================================================================================================== # ====================================================================================================
if cluster == 0x0008 # ========== Level Control 1.6 p.57 ========== if cluster == 0x0008 # ========== Level Control 1.6 p.57 ==========
self.update_shadow_lazy()
if command == 0x0000 # ---------- MoveToLevel ---------- if command == 0x0000 # ---------- MoveToLevel ----------
var bri_in = val.findsubval(0) # Hue 0..254 var bri_in = val.findsubval(0) # Hue 0..254
self.set_bri(bri_in) self.set_bri(bri_in)

View File

@ -95,7 +95,7 @@ class Matter_Plugin_Bridge_Light2 : Matter_Plugin_Bridge_Light1
############################################################# #############################################################
# read an attribute # read an attribute
# #
def read_attribute(session, ctx) def read_attribute(session, ctx, tlv_solo)
var TLV = matter.TLV var TLV = matter.TLV
var cluster = ctx.cluster var cluster = ctx.cluster
var attribute = ctx.attribute var attribute = ctx.attribute
@ -105,29 +105,29 @@ class Matter_Plugin_Bridge_Light2 : Matter_Plugin_Bridge_Light1
self.update_shadow_lazy() self.update_shadow_lazy()
if attribute == 0x0007 # ---------- ColorTemperatureMireds / u2 ---------- if attribute == 0x0007 # ---------- ColorTemperatureMireds / u2 ----------
if self.shadow_ct != nil if self.shadow_ct != nil
return TLV.create_TLV(TLV.U1, self.shadow_ct) return tlv_solo.set(TLV.U1, self.shadow_ct)
else else
return TLV.create_TLV(TLV.NULL, nil) return tlv_solo.set(TLV.NULL, nil)
end end
elif attribute == 0x0008 # ---------- ColorMode / u1 ---------- elif attribute == 0x0008 # ---------- ColorMode / u1 ----------
return TLV.create_TLV(TLV.U1, 2)# 2 = ColorTemperatureMireds return tlv_solo.set(TLV.U1, 2)# 2 = ColorTemperatureMireds
elif attribute == 0x000F # ---------- Options / u1 ---------- elif attribute == 0x000F # ---------- Options / u1 ----------
return TLV.create_TLV(TLV.U1, 0) return tlv_solo.set(TLV.U1, 0)
elif attribute == 0x400B # ---------- ColorTempPhysicalMinMireds / u2 ---------- elif attribute == 0x400B # ---------- ColorTempPhysicalMinMireds / u2 ----------
return TLV.create_TLV(TLV.U1, self.ct_min) return tlv_solo.set(TLV.U1, self.ct_min)
elif attribute == 0x400C # ---------- ColorTempPhysicalMaxMireds / u2 ---------- elif attribute == 0x400C # ---------- ColorTempPhysicalMaxMireds / u2 ----------
return TLV.create_TLV(TLV.U1, self.ct_max) return tlv_solo.set(TLV.U1, self.ct_max)
elif attribute == 0x400A # ---------- ColorCapabilities / map32 ---------- elif attribute == 0x400A # ---------- ColorCapabilities / map32 ----------
return TLV.create_TLV(TLV.U4, 0x10) # CT return tlv_solo.set(TLV.U4, 0x10) # CT
elif attribute == 0xFFFC # ---------- FeatureMap / map32 ---------- elif attribute == 0xFFFC # ---------- FeatureMap / map32 ----------
return TLV.create_TLV(TLV.U4, 0x10) # CT return tlv_solo.set(TLV.U4, 0x10) # CT
elif attribute == 0xFFFD # ---------- ClusterRevision / u2 ---------- elif attribute == 0xFFFD # ---------- ClusterRevision / u2 ----------
return TLV.create_TLV(TLV.U4, 5) # "new data model format and notation, FeatureMap support" return tlv_solo.set(TLV.U4, 5) # "new data model format and notation, FeatureMap support"
end end
else else
return super(self).read_attribute(session, ctx) return super(self).read_attribute(session, ctx, tlv_solo)
end end
end end
@ -143,7 +143,6 @@ class Matter_Plugin_Bridge_Light2 : Matter_Plugin_Bridge_Light1
# ==================================================================================================== # ====================================================================================================
if cluster == 0x0300 # ========== Color Control 3.2 p.111 ========== if cluster == 0x0300 # ========== Color Control 3.2 p.111 ==========
self.update_shadow_lazy()
if command == 0x000A # ---------- MoveToColorTemperature ---------- if command == 0x000A # ---------- MoveToColorTemperature ----------
var ct_in = val.findsubval(0) # CT var ct_in = val.findsubval(0) # CT
if ct_in < self.ct_min ct_in = self.ct_min end if ct_in < self.ct_min ct_in = self.ct_min end

View File

@ -95,7 +95,7 @@ class Matter_Plugin_Bridge_Light3 : Matter_Plugin_Bridge_Light1
############################################################# #############################################################
# read an attribute # read an attribute
# #
def read_attribute(session, ctx) def read_attribute(session, ctx, tlv_solo)
var TLV = matter.TLV var TLV = matter.TLV
var cluster = ctx.cluster var cluster = ctx.cluster
var attribute = ctx.attribute var attribute = ctx.attribute
@ -105,39 +105,39 @@ class Matter_Plugin_Bridge_Light3 : Matter_Plugin_Bridge_Light1
self.update_shadow_lazy() self.update_shadow_lazy()
if attribute == 0x0000 # ---------- CurrentHue / u1 ---------- if attribute == 0x0000 # ---------- CurrentHue / u1 ----------
if self.shadow_hue != nil if self.shadow_hue != nil
return TLV.create_TLV(TLV.U1, self.shadow_hue) return tlv_solo.set(TLV.U1, self.shadow_hue)
else else
return TLV.create_TLV(TLV.NULL, nil) return tlv_solo.set(TLV.NULL, nil)
end end
elif attribute == 0x0001 # ---------- CurrentSaturation / u2 ---------- elif attribute == 0x0001 # ---------- CurrentSaturation / u2 ----------
if self.shadow_sat != nil if self.shadow_sat != nil
return TLV.create_TLV(TLV.U1, self.shadow_sat) return tlv_solo.set(TLV.U1, self.shadow_sat)
else else
return TLV.create_TLV(TLV.NULL, nil) return tlv_solo.set(TLV.NULL, nil)
end end
elif attribute == 0x0007 # ---------- ColorTemperatureMireds / u2 ---------- elif attribute == 0x0007 # ---------- ColorTemperatureMireds / u2 ----------
return TLV.create_TLV(TLV.U1, 0) return tlv_solo.set(TLV.U1, 0)
elif attribute == 0x0008 # ---------- ColorMode / u1 ---------- elif attribute == 0x0008 # ---------- ColorMode / u1 ----------
return TLV.create_TLV(TLV.U1, 0)# 0 = CurrentHue and CurrentSaturation return tlv_solo.set(TLV.U1, 0)# 0 = CurrentHue and CurrentSaturation
elif attribute == 0x000F # ---------- Options / u1 ---------- elif attribute == 0x000F # ---------- Options / u1 ----------
return TLV.create_TLV(TLV.U1, 0) return tlv_solo.set(TLV.U1, 0)
elif attribute == 0x4001 # ---------- EnhancedColorMode / u1 ---------- elif attribute == 0x4001 # ---------- EnhancedColorMode / u1 ----------
return TLV.create_TLV(TLV.U1, 0) return tlv_solo.set(TLV.U1, 0)
elif attribute == 0x400A # ---------- ColorCapabilities / map32 ---------- elif attribute == 0x400A # ---------- ColorCapabilities / map32 ----------
return TLV.create_TLV(TLV.U4, 0x01) # HS return tlv_solo.set(TLV.U4, 0x01) # HS
# Defined Primaries Information Attribute Set # Defined Primaries Information Attribute Set
elif attribute == 0x0010 # ---------- NumberOfPrimaries / u1 ---------- elif attribute == 0x0010 # ---------- NumberOfPrimaries / u1 ----------
return TLV.create_TLV(TLV.U1, 0) return tlv_solo.set(TLV.U1, 0)
elif attribute == 0xFFFC # ---------- FeatureMap / map32 ---------- elif attribute == 0xFFFC # ---------- FeatureMap / map32 ----------
return TLV.create_TLV(TLV.U4, 0x01) # HS return tlv_solo.set(TLV.U4, 0x01) # HS
elif attribute == 0xFFFD # ---------- ClusterRevision / u2 ---------- elif attribute == 0xFFFD # ---------- ClusterRevision / u2 ----------
return TLV.create_TLV(TLV.U4, 5) # "new data model format and notation, FeatureMap support" return tlv_solo.set(TLV.U4, 5) # "new data model format and notation, FeatureMap support"
end end
else else
return super(self).read_attribute(session, ctx) return super(self).read_attribute(session, ctx, tlv_solo)
end end
end end
@ -153,7 +153,6 @@ class Matter_Plugin_Bridge_Light3 : Matter_Plugin_Bridge_Light1
# ==================================================================================================== # ====================================================================================================
if cluster == 0x0300 # ========== Color Control 3.2 p.111 ========== if cluster == 0x0300 # ========== Color Control 3.2 p.111 ==========
self.update_shadow_lazy()
if command == 0x0000 # ---------- MoveToHue ---------- if command == 0x0000 # ---------- MoveToHue ----------
var hue_in = val.findsubval(0) # Hue 0..254 var hue_in = val.findsubval(0) # Hue 0..254
self.set_hue(hue_in) self.set_hue(hue_in)

Some files were not shown because too many files have changed in this diff Show More