From e97254ad22567d43429f097072caf40ba6a6cdb8 Mon Sep 17 00:00:00 2001 From: JanRuijtenberg Date: Tue, 8 Oct 2024 14:00:56 +0200 Subject: [PATCH 1/9] add tasmota blueprint --- relays/tasmota/README.md | 30 ++++++++++++++ relays/tasmota/firmware.lua | 68 +++++++++++++++++++++++++++++++ relays/tasmota/manifest.yml | 81 +++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 relays/tasmota/README.md create mode 100644 relays/tasmota/firmware.lua create mode 100644 relays/tasmota/manifest.yml diff --git a/relays/tasmota/README.md b/relays/tasmota/README.md new file mode 100644 index 00000000..bdfacda6 --- /dev/null +++ b/relays/tasmota/README.md @@ -0,0 +1,30 @@ +# Generic Tasmota + +This [Enapter Device Blueprint](https://github.com/Enapter/marketplace#blue_book-enapter-device-blueprints) is meant to integrate with any [Tasmota](https://tasmota.github.io/docs/) device. **Tasmota** is opensource firmware, it runs on the ESP32 & ESP8266 chip. Tasmota firmware forms the basis for a lot of smart relays, smart sensors, smart sockets including loads of commercial vendors like [Nous](https://nous.technology/). But the ESP32 & ESP8266 essentially are very small webservers on a chip, and you can include them in any circuit board, so the combination of a chip, the tasmota firmware, and this blue print enables you to connect any device to the enapter cloud. + +The blueprint essentially is a wrapper around the Tasmota interface. It enables all commands which are supported by the tasmota device. This makes it a very powerful interface, but it also exposes a potential security thread. You should probably disable some commands on the tasmota firmware, or disable the generic command in this blueprint and only enable the once for your use case. + +## Setup + +The Tasmota device and the gateway must be able to communicate with each other over a local IP, so make sure that they are linked to the same network. + +For any specific questions about Tasmota look at the [Tasmota docs](https://tasmota.github.io/docs/) + +## Set up config + +Make sure to configure the device before you start using it, using the configure command. The configuration has two parameters + +1. The ADDRESS property should be filled with the local IP address for example 192.168.8.212 over which the gateway can connect to the device +2. The PASSWORD property is optional, it is only required if the Tasmota is set up in such a way that a password is required to connect to the api. Be aware that it is only a very thin layer of extra security and you should not depend purely on this feature for your security strategy + +## Command + +There are three commands to control the device + +1. turn_on will turn on the device +2. turn_off will turn the device off +3. tasmota_command is a generic command. You can pass in any commands supported by your device, a full list of commands can be found here [Tasmota Commands](https://tasmota.github.io/docs/Commands). Some commands are supported by all tasmota devices, but others are device specific the command MP3Play for example will play a song on a mp3 player running on Tasmota, and obviously won't have any effect on a smart plug. + +## Telemetry + +The blueprint sends one telemetry input showing if the device is on or off. There is much more telemetry data available, you should probably modify the example for your own use case. diff --git a/relays/tasmota/firmware.lua b/relays/tasmota/firmware.lua new file mode 100644 index 00000000..b95001a6 --- /dev/null +++ b/relays/tasmota/firmware.lua @@ -0,0 +1,68 @@ +json = require("json") +connection = http.client({timeout = 10}) +local config = require('enapter.ucm.config') + +-- The addres is the local IP adres of the tasmota device +-- The password is optional it is only needed in case the web-admin pasword is set on the tasmota device, be aware that this is only a very thin extra layer of security and you should not purely rely on that. +ADDRESS = 'address' +PASSWORD = 'password' + +function main() + config.init({ + [PASSWORD] = {type = 'string', required = false}, + [ADDRESS] = { type = 'string', required = true, default = '127.0.0.1' }, + }) + enapter.register_command_handler("turn_on", function() return control_device("Power%20On") end) + enapter.register_command_handler("turn_off", function() return control_device("Power%20Off") end) + enapter.register_command_handler("tasmota_command", tasmota_command) + scheduler.add(30000, send_properties) + scheduler.add(1000, send_telemetry) +end + +function tasmota_command(ctx, args) + enapter.log("command arrived " .. args["command"]) + return control_device(args["command"]) +end + +function send_properties() + enapter.send_properties({ + model = 'TASMOTA' + }) +end + +-- Sends status, change for your specific requirements +function send_telemetry() + local tasmota_status = control_device("Status0") + local decoded_status = json.decode(tasmota_status) -- The status line contains all kind of info + local power = decoded_status.Status.Power -- Access the "Power" field + local power_bool = (power == 1) + enapter.send_telemetry({is_on = power_bool}) +end + +-- Generic function to control the device +function control_device(command) + local config_values, err = config.read_all() + + local url = "http://" .. config_values[ADDRESS] .. "/cm?cmnd=" .. command + + -- Add user and password only if password is provided + if config_values[PASSWORD] and config_values[PASSWORD] ~= "" then + url = url .. "&user=admin&password=" .. config_values[PASSWORD] + end + + local response, error = connection:get(url) + + if error then + enapter.log("HTTP request failed: " .. error) + return error + end + + if response and response.body then + enapter.log("Result for command (".. command .. "): " .. response.body) + return response.body; + else + enapter.log("No response body or invalid response format for command: " .. command) + end +end + +main() diff --git a/relays/tasmota/manifest.yml b/relays/tasmota/manifest.yml new file mode 100644 index 00000000..ecc459ef --- /dev/null +++ b/relays/tasmota/manifest.yml @@ -0,0 +1,81 @@ +# Manifest for blueprint connection to any Tasmota device + +blueprint_spec: device/1.0 + +display_name: tasmota device +icon: light-switch +vendor: tasmota +author: Hermanos Energy +contributors: + - Jan Ruijtenberg +license: MIT + + +communication_module: + product: ENP-VIRTUAL + lua: + file: firmware.lua + dependencies: + - enapter-ucm + +properties: + serial_number: + type: string + display_name: Serial Number + model: + type: string + display_name: Model + +telemetry: + is_on: + type: boolean + display_name: Is on + +commands: + turn_on: + display_name: Turn on + group: tasmota + ui: + icon: led-on + quick_access: true + turn_off: + display_name: Turn off + group: tasmota + ui: + icon: led-of + quick_access: true + tasmota_command: + arguments: + command: + display_name: command + description: A valid tasmota command, + type: string + required: true + display_name: Send any command + group: tasmota + write_configuration: + display_name: Configure + group: config + populate_values_command: read_configuration + ui: + icon: file-document-edit-outline + arguments: + address: + display_name: ip address + type: string + required: true + password: + display_name: password + type: string + required: false + read_configuration: + display_name: Read config Parameters + group: config + ui: + icon: file-check-outline + +command_groups: + config: + display_name: Configuration + tasmota: + display_name: Tasmota commands From bcd597fc3d8941e3db2c1f77d654bb3501d73047 Mon Sep 17 00:00:00 2001 From: JanRuijtenberg Date: Tue, 8 Oct 2024 14:21:19 +0200 Subject: [PATCH 2/9] fix linting errors in manifest --- relays/tasmota/manifest.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/relays/tasmota/manifest.yml b/relays/tasmota/manifest.yml index ecc459ef..ef535814 100644 --- a/relays/tasmota/manifest.yml +++ b/relays/tasmota/manifest.yml @@ -2,10 +2,14 @@ blueprint_spec: device/1.0 -display_name: tasmota device +display_name: Tasmota device +description: A wrapper around the Tasmota interface to connect to any Tasmota powered device. icon: light-switch -vendor: tasmota +vendor: Tasmota author: Hermanos Energy +support: + url: https://go.enapter.com/enapter-blueprint-support + email: support@enapter.com contributors: - Jan Ruijtenberg license: MIT From 0142f0b57c0b1d1484adc4660938604a8cab9304 Mon Sep 17 00:00:00 2001 From: JanRuijtenberg Date: Tue, 8 Oct 2024 14:31:00 +0200 Subject: [PATCH 3/9] format lua file with StyLua --- relays/tasmota/firmware.lua | 46 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/relays/tasmota/firmware.lua b/relays/tasmota/firmware.lua index b95001a6..c91958ad 100644 --- a/relays/tasmota/firmware.lua +++ b/relays/tasmota/firmware.lua @@ -1,5 +1,5 @@ -json = require("json") -connection = http.client({timeout = 10}) +json = require('json') +connection = http.client({ timeout = 10 }) local config = require('enapter.ucm.config') -- The addres is the local IP adres of the tasmota device @@ -9,59 +9,63 @@ PASSWORD = 'password' function main() config.init({ - [PASSWORD] = {type = 'string', required = false}, + [PASSWORD] = { type = 'string', required = false }, [ADDRESS] = { type = 'string', required = true, default = '127.0.0.1' }, }) - enapter.register_command_handler("turn_on", function() return control_device("Power%20On") end) - enapter.register_command_handler("turn_off", function() return control_device("Power%20Off") end) - enapter.register_command_handler("tasmota_command", tasmota_command) + enapter.register_command_handler('turn_on', function() + return control_device('Power%20On') + end) + enapter.register_command_handler('turn_off', function() + return control_device('Power%20Off') + end) + enapter.register_command_handler('tasmota_command', tasmota_command) scheduler.add(30000, send_properties) scheduler.add(1000, send_telemetry) end function tasmota_command(ctx, args) - enapter.log("command arrived " .. args["command"]) - return control_device(args["command"]) + enapter.log('command arrived ' .. args['command']) + return control_device(args['command']) end function send_properties() enapter.send_properties({ - model = 'TASMOTA' + model = 'TASMOTA', }) end -- Sends status, change for your specific requirements function send_telemetry() - local tasmota_status = control_device("Status0") - local decoded_status = json.decode(tasmota_status) -- The status line contains all kind of info - local power = decoded_status.Status.Power -- Access the "Power" field - local power_bool = (power == 1) - enapter.send_telemetry({is_on = power_bool}) + local tasmota_status = control_device('Status0') + local decoded_status = json.decode(tasmota_status) -- The status line contains all kind of info + local power = decoded_status.Status.Power -- Access the "Power" field + local power_bool = (power == 1) + enapter.send_telemetry({ is_on = power_bool }) end -- Generic function to control the device function control_device(command) local config_values, err = config.read_all() - local url = "http://" .. config_values[ADDRESS] .. "/cm?cmnd=" .. command + local url = 'http://' .. config_values[ADDRESS] .. '/cm?cmnd=' .. command -- Add user and password only if password is provided - if config_values[PASSWORD] and config_values[PASSWORD] ~= "" then - url = url .. "&user=admin&password=" .. config_values[PASSWORD] + if config_values[PASSWORD] and config_values[PASSWORD] ~= '' then + url = url .. '&user=admin&password=' .. config_values[PASSWORD] end local response, error = connection:get(url) if error then - enapter.log("HTTP request failed: " .. error) + enapter.log('HTTP request failed: ' .. error) return error end if response and response.body then - enapter.log("Result for command (".. command .. "): " .. response.body) - return response.body; + enapter.log('Result for command (' .. command .. '): ' .. response.body) + return response.body else - enapter.log("No response body or invalid response format for command: " .. command) + enapter.log('No response body or invalid response format for command: ' .. command) end end From 764a40e7da2bf76d84e17ad0097b4764c519bb1b Mon Sep 17 00:00:00 2001 From: JanRuijtenberg Date: Tue, 8 Oct 2024 14:45:56 +0200 Subject: [PATCH 4/9] fix some more linting --- relays/tasmota/README.md | 18 +++++++++--------- relays/tasmota/firmware.lua | 9 +++++++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/relays/tasmota/README.md b/relays/tasmota/README.md index bdfacda6..e16392b6 100644 --- a/relays/tasmota/README.md +++ b/relays/tasmota/README.md @@ -1,21 +1,21 @@ # Generic Tasmota -This [Enapter Device Blueprint](https://github.com/Enapter/marketplace#blue_book-enapter-device-blueprints) is meant to integrate with any [Tasmota](https://tasmota.github.io/docs/) device. **Tasmota** is opensource firmware, it runs on the ESP32 & ESP8266 chip. Tasmota firmware forms the basis for a lot of smart relays, smart sensors, smart sockets including loads of commercial vendors like [Nous](https://nous.technology/). But the ESP32 & ESP8266 essentially are very small webservers on a chip, and you can include them in any circuit board, so the combination of a chip, the tasmota firmware, and this blue print enables you to connect any device to the enapter cloud. +This [Enapter Device Blueprint](https://github.com/Enapter/marketplace#blue_book-enapter-device-blueprints) is meant to integrate with any [Tasmota](https://tasmota.github.io/docs/) device. **Tasmota** is opensource firmware, it runs on the ESP32 & ESP8266 chip. Tasmota firmware forms the basis for a lot of smart relays, smart sensors, smart sockets including loads of commercial vendors like [Nous](https://nous.technology/). But the ESP32 & ESP8266 essentially are very small webservers on a chip, and you can include them in any circuit board, so the combination of a chip, the tasmota firmware, and this blue print enables you to connect any device to the enapter cloud. -The blueprint essentially is a wrapper around the Tasmota interface. It enables all commands which are supported by the tasmota device. This makes it a very powerful interface, but it also exposes a potential security thread. You should probably disable some commands on the tasmota firmware, or disable the generic command in this blueprint and only enable the once for your use case. +The blueprint essentially is a wrapper around the Tasmota interface. It enables all commands which are supported by the tasmota device. This makes it a very powerful interface, but it also exposes a potential security thread. You should probably disable some commands on the tasmota firmware, or disable the generic command in this blueprint and only enable the once for your use case. ## Setup -The Tasmota device and the gateway must be able to communicate with each other over a local IP, so make sure that they are linked to the same network. +The Tasmota device and the gateway must be able to communicate with each other over a local IP, so make sure that they are linked to the same network. -For any specific questions about Tasmota look at the [Tasmota docs](https://tasmota.github.io/docs/) +For any specific questions about Tasmota look at the [Tasmota docs](https://tasmota.github.io/docs/). ## Set up config -Make sure to configure the device before you start using it, using the configure command. The configuration has two parameters +Make sure to configure the device before you start using it, using the configure command. The configuration has two parameters: -1. The ADDRESS property should be filled with the local IP address for example 192.168.8.212 over which the gateway can connect to the device -2. The PASSWORD property is optional, it is only required if the Tasmota is set up in such a way that a password is required to connect to the api. Be aware that it is only a very thin layer of extra security and you should not depend purely on this feature for your security strategy +1. The ADDRESS property should be filled with the local IP address for example 192.168.8.212 over which the gateway can connect to the device. +2. The PASSWORD property is optional, it is only required if the Tasmota is set up in such a way that a password is required to connect to the api. Be aware that it is only a very thin layer of extra security and you should not depend purely on this feature for your security strategy. ## Command @@ -23,8 +23,8 @@ There are three commands to control the device 1. turn_on will turn on the device 2. turn_off will turn the device off -3. tasmota_command is a generic command. You can pass in any commands supported by your device, a full list of commands can be found here [Tasmota Commands](https://tasmota.github.io/docs/Commands). Some commands are supported by all tasmota devices, but others are device specific the command MP3Play for example will play a song on a mp3 player running on Tasmota, and obviously won't have any effect on a smart plug. +3. tasmota_command is a generic command. You can pass in any commands supported by your device, a full list of commands can be found here [Tasmota Commands](https://tasmota.github.io/docs/Commands). Some commands are supported by all tasmota devices, but others are device specific the command MP3Play for example will play a song on a mp3 player running on Tasmota, and obviously won't have any effect on a smart plug. ## Telemetry -The blueprint sends one telemetry input showing if the device is on or off. There is much more telemetry data available, you should probably modify the example for your own use case. +The blueprint sends one telemetry input showing if the device is on or off. There is much more telemetry data available, you should probably modify the example for your own use case. diff --git a/relays/tasmota/firmware.lua b/relays/tasmota/firmware.lua index c91958ad..15e5bc67 100644 --- a/relays/tasmota/firmware.lua +++ b/relays/tasmota/firmware.lua @@ -3,7 +3,8 @@ connection = http.client({ timeout = 10 }) local config = require('enapter.ucm.config') -- The addres is the local IP adres of the tasmota device --- The password is optional it is only needed in case the web-admin pasword is set on the tasmota device, be aware that this is only a very thin extra layer of security and you should not purely rely on that. +-- The password is optional it is only needed in case the web-admin pasword is set on the tasmota device, +-- be aware that this is only a very thin extra layer of security and you should not purely rely on that. ADDRESS = 'address' PASSWORD = 'password' @@ -23,7 +24,7 @@ function main() scheduler.add(1000, send_telemetry) end -function tasmota_command(ctx, args) +function tasmota_command(args) enapter.log('command arrived ' .. args['command']) return control_device(args['command']) end @@ -47,6 +48,10 @@ end function control_device(command) local config_values, err = config.read_all() + if err then + enapter.log('Could not read config: ' .. err) + end + local url = 'http://' .. config_values[ADDRESS] .. '/cm?cmnd=' .. command -- Add user and password only if password is provided From da40a89ae18c510bdba608ee4ece0485449ed50b Mon Sep 17 00:00:00 2001 From: JanRuijtenberg Date: Tue, 8 Oct 2024 14:55:09 +0200 Subject: [PATCH 5/9] reformat --- relays/tasmota/firmware.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relays/tasmota/firmware.lua b/relays/tasmota/firmware.lua index 15e5bc67..5af9110b 100644 --- a/relays/tasmota/firmware.lua +++ b/relays/tasmota/firmware.lua @@ -3,7 +3,7 @@ connection = http.client({ timeout = 10 }) local config = require('enapter.ucm.config') -- The addres is the local IP adres of the tasmota device --- The password is optional it is only needed in case the web-admin pasword is set on the tasmota device, +-- The password is optional it is only needed in case the web-admin pasword is set on the tasmota device, -- be aware that this is only a very thin extra layer of security and you should not purely rely on that. ADDRESS = 'address' PASSWORD = 'password' From a5cef5218e073689e048b61969c9fe0e0065fdcc Mon Sep 17 00:00:00 2001 From: JanRuijtenberg Date: Tue, 8 Oct 2024 15:00:06 +0200 Subject: [PATCH 6/9] remove trailing white space in readme --- relays/tasmota/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/relays/tasmota/README.md b/relays/tasmota/README.md index e16392b6..7337f43d 100644 --- a/relays/tasmota/README.md +++ b/relays/tasmota/README.md @@ -1,4 +1,4 @@ -# Generic Tasmota +# Generic Tasmota This [Enapter Device Blueprint](https://github.com/Enapter/marketplace#blue_book-enapter-device-blueprints) is meant to integrate with any [Tasmota](https://tasmota.github.io/docs/) device. **Tasmota** is opensource firmware, it runs on the ESP32 & ESP8266 chip. Tasmota firmware forms the basis for a lot of smart relays, smart sensors, smart sockets including loads of commercial vendors like [Nous](https://nous.technology/). But the ESP32 & ESP8266 essentially are very small webservers on a chip, and you can include them in any circuit board, so the combination of a chip, the tasmota firmware, and this blue print enables you to connect any device to the enapter cloud. @@ -6,7 +6,7 @@ The blueprint essentially is a wrapper around the Tasmota interface. It enables ## Setup -The Tasmota device and the gateway must be able to communicate with each other over a local IP, so make sure that they are linked to the same network. +The Tasmota device and the gateway must be able to communicate with each other over a local IP, so make sure that they are linked to the same network. For any specific questions about Tasmota look at the [Tasmota docs](https://tasmota.github.io/docs/). From bf1cf0f73ffbe37bd151f47bb4d70b9c41f3f520 Mon Sep 17 00:00:00 2001 From: JanRuijtenberg Date: Wed, 9 Oct 2024 11:27:42 +0200 Subject: [PATCH 7/9] add vendor --- .marketplace/vendors/vendors.yml | 5 +++++ relays/tasmota/manifest.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.marketplace/vendors/vendors.yml b/.marketplace/vendors/vendors.yml index 66f8a36f..5433c5ea 100644 --- a/.marketplace/vendors/vendors.yml +++ b/.marketplace/vendors/vendors.yml @@ -252,3 +252,8 @@ display_name: EFOY icon_url: https://raw.githubusercontent.com/Enapter/marketplace/main/.marketplace/vendors/icons/efoy.png website: https://www.efoy.com/ + +- id: tasmota + display_name: Tasmota + icon_url: https://raw.githubusercontent.com/Enapter/marketplace/main/.marketplace/vendors/icons/tasmota.png + website: https://tasmota.github.io/docs/ \ No newline at end of file diff --git a/relays/tasmota/manifest.yml b/relays/tasmota/manifest.yml index ef535814..eafd5321 100644 --- a/relays/tasmota/manifest.yml +++ b/relays/tasmota/manifest.yml @@ -5,7 +5,7 @@ blueprint_spec: device/1.0 display_name: Tasmota device description: A wrapper around the Tasmota interface to connect to any Tasmota powered device. icon: light-switch -vendor: Tasmota +vendor: tasmota author: Hermanos Energy support: url: https://go.enapter.com/enapter-blueprint-support From 91173c26301f7cfcd18a2c01634758799702551c Mon Sep 17 00:00:00 2001 From: JanRuijtenberg Date: Wed, 9 Oct 2024 12:14:49 +0200 Subject: [PATCH 8/9] response to code review --- .marketplace/vendors/icons/tasmota.png | Bin 0 -> 8481 bytes .marketplace/vendors/vendors.yml | 3 ++- relays/tasmota/firmware.lua | 8 ++++---- relays/tasmota/manifest.yml | 13 ++++++++----- 4 files changed, 14 insertions(+), 10 deletions(-) create mode 100644 .marketplace/vendors/icons/tasmota.png diff --git a/.marketplace/vendors/icons/tasmota.png b/.marketplace/vendors/icons/tasmota.png new file mode 100644 index 0000000000000000000000000000000000000000..9e61cfe45c72040fa4d680675597908141534236 GIT binary patch literal 8481 zcmZ`h#)Pgh)SoF0y27ZNh2yLDWM?J-Q5F}MjFZA zqu;-Oe{4JVzMk_w=f3Z`=bXDOO!uA|H3cgL0RaKEhB`)%fB=L8K=efEt`Y%3c?{)+B{2a3yha0~r0)gV z&RB?JZ%b@%+OY2`-ZFDwq;^o7)Xj6>B4(=jev%pI>lhLB$Vv)q_soj`=S5J^M#%h| z@ToELuIgtf*}4B%)GkwNRM~TP#H$Imz|L~PEsZ=t_9z{qH9LHHf5%R@uk zhjy!mzuxse;3P}6z9{kxJYSC5Bde`DTe}HCNA4+hz7u7LiDoT|I?d9&wM(lK3~}X! z+XzkZaFWwQ(Rzv?glh*BAFBvOv_dGzQu)RICldfN8&wu9C zqWW?d??2_}Krov1?!7(rY%Iiuj2<%lh=MkTD4Yy&o*jr&)%)zlt&1U>A|?(NkB)2o zi_}R^L1s^XGvY521ON*9%>5Tg^rT8rov;(Z+5KSIorbRj;+6S9k~82SiMkxws=*7- zKVG4aVagrrs6RDmP=+G7IovE`jUQmWw&cgZS*kT_`3cllcFLkbi0b$1dy}r(fD5Vs zyx=Wm*<gTd%$L3aVQdw^Q#Uyy7H zVDu6gk!A-%etpuDJs$n*JSQ)_)!q~mBj=ujh3p#wBO_BKUpZqoZm?QW3-o_9-{Z3JL6yCQq_RP>ZQT|k>Uuf zz*byCmX;T3lL-*2`84-0p`BlWEgQG5_$SK(vOy~8-yOgeC{$W3tn@KKx)}9%0AA0@OjcSm=auay_oyL{=k@i_nI~5Q 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 Date: Wed, 9 Oct 2024 14:22:11 +0200 Subject: [PATCH 9/9] add device --- .marketplace/devices/devices.yml | 10 ++++++++++ .marketplace/vendors/vendors.yml | 1 - 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.marketplace/devices/devices.yml b/.marketplace/devices/devices.yml index 5e87cb44..819d0635 100644 --- a/.marketplace/devices/devices.yml +++ b/.marketplace/devices/devices.yml @@ -797,3 +797,13 @@ blueprint_options: - blueprint: fuel_cells/efoy_h2_cabinet_n_series verification_level: ready_for_testing + +- id: tasmota + display_name: Tasmota + description: Any product running on Tasmota firmware + icon: enapter-microchip + vendor: tasmota + category: relays + blueprint_options: + - blueprint: relays/tasmota + verification_level: ready_for_testing diff --git a/.marketplace/vendors/vendors.yml b/.marketplace/vendors/vendors.yml index ee9b7067..91174023 100644 --- a/.marketplace/vendors/vendors.yml +++ b/.marketplace/vendors/vendors.yml @@ -257,4 +257,3 @@ display_name: Tasmota icon_url: https://raw.githubusercontent.com/Enapter/marketplace/main/.marketplace/vendors/icons/tasmota.png website: https://tasmota.github.io/docs/ - \ No newline at end of file