Add support for Shelly Dimmer 2 (#2954)

Co-authored-by: Niclas Larsson <niclas@edgesystems.se>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Jernej Kos <jernej@kos.mx>
Co-authored-by: Richard Nauber <richard@nauber.dev>
This commit is contained in:
rnauber
2022-04-14 03:13:51 +02:00
committed by GitHub
parent 047c18eac0
commit 70a35656e4
11 changed files with 2232 additions and 0 deletions
+1
View File
@@ -173,6 +173,7 @@ esphome/components/select/* @esphome/core
esphome/components/sensirion_common/* @martgras
esphome/components/sensor/* @esphome/core
esphome/components/sgp40/* @SenexCrenshaw
esphome/components/shelly_dimmer/* @edge90 @rnauber
esphome/components/sht4x/* @sjtrny
esphome/components/shutdown/* @esphome/core @jsuanet
esphome/components/sim800l/* @glmnet
@@ -0,0 +1,2 @@
The firmware files for the STM microcontroller (shelly-dimmer-stm32_*.bin) are taken from
https://github.com/jamesturton/shelly-dimmer-stm32 and GPLv3 licensed.
@@ -0,0 +1 @@
CODEOWNERS = ["@rnauber", "@edge90"]
@@ -0,0 +1,158 @@
/*
stm32flash - Open Source ST STM32 flash program for Arduino
Copyright (C) 2010 Geoffrey McRae <geoff@spacevs.com>
Copyright (C) 2014-2015 Antonio Borneo <borneo.antonio@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_SHD_FIRMWARE_DATA
#include "stm32flash.h"
namespace esphome {
namespace shelly_dimmer {
constexpr uint32_t SZ_128 = 0x00000080;
constexpr uint32_t SZ_256 = 0x00000100;
constexpr uint32_t SZ_1K = 0x00000400;
constexpr uint32_t SZ_2K = 0x00000800;
constexpr uint32_t SZ_16K = 0x00004000;
constexpr uint32_t SZ_32K = 0x00008000;
constexpr uint32_t SZ_64K = 0x00010000;
constexpr uint32_t SZ_128K = 0x00020000;
constexpr uint32_t SZ_256K = 0x00040000;
/*
* Page-size for page-by-page flash erase.
* Arrays are zero terminated; last non-zero value is automatically repeated
*/
/* fixed size pages */
constexpr uint32_t p_128[] = {SZ_128, 0}; // NOLINT
constexpr uint32_t p_256[] = {SZ_256, 0}; // NOLINT
constexpr uint32_t p_1k[] = {SZ_1K, 0}; // NOLINT
constexpr uint32_t p_2k[] = {SZ_2K, 0}; // NOLINT
/* F2 and F4 page size */
constexpr uint32_t f2f4[] = {SZ_16K, SZ_16K, SZ_16K, SZ_16K, SZ_64K, SZ_128K, 0}; // NOLINT
/* F4 dual bank page size */
constexpr uint32_t f4db[] = {SZ_16K, SZ_16K, SZ_16K, SZ_16K, SZ_64K, SZ_128K, SZ_128K, // NOLINT
SZ_128K, SZ_16K, SZ_16K, SZ_16K, SZ_16K, SZ_64K, SZ_128K, 0};
/* F7 page size */
constexpr uint32_t f7[] = {SZ_32K, SZ_32K, SZ_32K, SZ_32K, SZ_128K, SZ_256K, 0}; // NOLINT
/*
* Device table, corresponds to the "Bootloader device-dependant parameters"
* table in ST document AN2606.
* Note that the option bytes upper range is inclusive!
*/
constexpr stm32_dev_t DEVICES[] = {
/* ID "name" SRAM-address-range FLASH-address-range PPS PSize
Option-byte-addr-range System-mem-addr-range Flags */
/* F0 */
{0x440, "STM32F030x8/F05xxx", 0x20000800, 0x20002000, 0x08000000, 0x08010000, 4, p_1k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFEC00, 0x1FFFF800, 0},
{0x442, "STM32F030xC/F09xxx", 0x20001800, 0x20008000, 0x08000000, 0x08040000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFC800, 0x1FFFF800, F_OBLL},
{0x444, "STM32F03xx4/6", 0x20000800, 0x20001000, 0x08000000, 0x08008000, 4, p_1k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFEC00, 0x1FFFF800, 0},
{0x445, "STM32F04xxx/F070x6", 0x20001800, 0x20001800, 0x08000000, 0x08008000, 4, p_1k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFC400, 0x1FFFF800, 0},
{0x448, "STM32F070xB/F071xx/F72xx", 0x20001800, 0x20004000, 0x08000000, 0x08020000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFC800, 0x1FFFF800, 0},
/* F1 */
{0x412, "STM32F10xxx Low-density", 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, p_1k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFF000, 0x1FFFF800, 0},
{0x410, "STM32F10xxx Medium-density", 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, p_1k, 0x1FFFF800,
0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0},
{0x414, "STM32F10xxx High-density", 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFF000, 0x1FFFF800, 0},
{0x420, "STM32F10xxx Medium-density VL", 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, p_1k, 0x1FFFF800,
0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0},
{0x428, "STM32F10xxx High-density VL", 0x20000200, 0x20008000, 0x08000000, 0x08080000, 2, p_2k, 0x1FFFF800,
0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0},
{0x418, "STM32F105xx/F107xx", 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFB000, 0x1FFFF800, 0},
{0x430, "STM32F10xxx XL-density", 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFE000, 0x1FFFF800, 0},
/* F2 */
{0x411, "STM32F2xxxx", 0x20002000, 0x20020000, 0x08000000, 0x08100000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000,
0x1FFF7800, 0},
/* F3 */
{0x432, "STM32F373xx/F378xx", 0x20001400, 0x20008000, 0x08000000, 0x08040000, 2, p_2k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFD800, 0x1FFFF800, 0},
{0x422, "STM32F302xB(C)/F303xB(C)/F358xx", 0x20001400, 0x2000A000, 0x08000000, 0x08040000, 2, p_2k, 0x1FFFF800,
0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0},
{0x439, "STM32F301xx/F302x4(6/8)/F318xx", 0x20001800, 0x20004000, 0x08000000, 0x08010000, 2, p_2k, 0x1FFFF800,
0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0},
{0x438, "STM32F303x4(6/8)/F334xx/F328xx", 0x20001800, 0x20003000, 0x08000000, 0x08010000, 2, p_2k, 0x1FFFF800,
0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0},
{0x446, "STM32F302xD(E)/F303xD(E)/F398xx", 0x20001800, 0x20010000, 0x08000000, 0x08080000, 2, p_2k, 0x1FFFF800,
0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0},
/* F4 */
{0x413, "STM32F40xxx/41xxx", 0x20003000, 0x20020000, 0x08000000, 0x08100000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F,
0x1FFF0000, 0x1FFF7800, 0},
{0x419, "STM32F42xxx/43xxx", 0x20003000, 0x20030000, 0x08000000, 0x08200000, 1, f4db, 0x1FFEC000, 0x1FFFC00F,
0x1FFF0000, 0x1FFF7800, 0},
{0x423, "STM32F401xB(C)", 0x20003000, 0x20010000, 0x08000000, 0x08040000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F,
0x1FFF0000, 0x1FFF7800, 0},
{0x433, "STM32F401xD(E)", 0x20003000, 0x20018000, 0x08000000, 0x08080000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F,
0x1FFF0000, 0x1FFF7800, 0},
{0x458, "STM32F410xx", 0x20003000, 0x20008000, 0x08000000, 0x08020000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000,
0x1FFF7800, 0},
{0x431, "STM32F411xx", 0x20003000, 0x20020000, 0x08000000, 0x08080000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000,
0x1FFF7800, 0},
{0x421, "STM32F446xx", 0x20003000, 0x20020000, 0x08000000, 0x08080000, 1, f2f4, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000,
0x1FFF7800, 0},
{0x434, "STM32F469xx", 0x20003000, 0x20060000, 0x08000000, 0x08200000, 1, f4db, 0x1FFEC000, 0x1FFFC00F, 0x1FFF0000,
0x1FFF7800, 0},
/* F7 */
{0x449, "STM32F74xxx/75xxx", 0x20004000, 0x20050000, 0x08000000, 0x08100000, 1, f7, 0x1FFF0000, 0x1FFF001F,
0x1FF00000, 0x1FF0EDC0, 0},
/* L0 */
{0x425, "STM32L031xx/041xx", 0x20001000, 0x20002000, 0x08000000, 0x08008000, 32, p_128, 0x1FF80000, 0x1FF8001F,
0x1FF00000, 0x1FF01000, 0},
{0x417, "STM32L05xxx/06xxx", 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, p_128, 0x1FF80000, 0x1FF8001F,
0x1FF00000, 0x1FF01000, 0},
{0x447, "STM32L07xxx/08xxx", 0x20002000, 0x20005000, 0x08000000, 0x08030000, 32, p_128, 0x1FF80000, 0x1FF8001F,
0x1FF00000, 0x1FF02000, 0},
/* L1 */
{0x416, "STM32L1xxx6(8/B)", 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, p_256, 0x1FF80000, 0x1FF8001F,
0x1FF00000, 0x1FF01000, F_NO_ME},
{0x429, "STM32L1xxx6(8/B)A", 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, p_256, 0x1FF80000, 0x1FF8001F,
0x1FF00000, 0x1FF01000, 0},
{0x427, "STM32L1xxxC", 0x20001000, 0x20008000, 0x08000000, 0x08040000, 16, p_256, 0x1FF80000, 0x1FF8001F,
0x1FF00000, 0x1FF02000, 0},
{0x436, "STM32L1xxxD", 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, p_256, 0x1FF80000, 0x1FF8009F,
0x1FF00000, 0x1FF02000, 0},
{0x437, "STM32L1xxxE", 0x20001000, 0x20014000, 0x08000000, 0x08080000, 16, p_256, 0x1FF80000, 0x1FF8009F,
0x1FF00000, 0x1FF02000, F_NO_ME},
/* L4 */
{0x415, "STM32L476xx/486xx", 0x20003100, 0x20018000, 0x08000000, 0x08100000, 1, p_2k, 0x1FFF7800, 0x1FFFF80F,
0x1FFF0000, 0x1FFF7000, 0},
/* These are not (yet) in AN2606: */
{0x641, "Medium_Density PL", 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, p_1k, 0x1FFFF800, 0x1FFFF80F,
0x1FFFF000, 0x1FFFF800, 0},
{0x9a8, "STM32W-128K", 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, p_1k, 0x08040800, 0x0804080F, 0x08040000,
0x08040800, 0},
{0x9b0, "STM32W-256K", 0x20000200, 0x20004000, 0x08000000, 0x08040000, 4, p_2k, 0x08040800, 0x0804080F, 0x08040000,
0x08040800, 0},
{0x0, "", 0x0, 0x0, 0x0, 0x0, 0x0, nullptr, 0x0, 0x0, 0x0, 0x0, 0x0},
};
} // namespace shelly_dimmer
} // namespace esphome
#endif
+219
View File
@@ -0,0 +1,219 @@
from pathlib import Path
import hashlib
import re
import requests
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import light, sensor, uart
from esphome.const import (
CONF_OUTPUT_ID,
CONF_GAMMA_CORRECT,
CONF_POWER,
CONF_VOLTAGE,
CONF_CURRENT,
CONF_VERSION,
CONF_URL,
CONF_UPDATE_INTERVAL,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
)
from esphome.core import HexInt, CORE
DOMAIN = "shelly_dimmer"
DEPENDENCIES = ["sensor", "uart"]
shelly_dimmer_ns = cg.esphome_ns.namespace("shelly_dimmer")
ShellyDimmer = shelly_dimmer_ns.class_(
"ShellyDimmer", light.LightOutput, cg.PollingComponent, uart.UARTDevice
)
CONF_FIRMWARE = "firmware"
CONF_SHA256 = "sha256"
CONF_UPDATE = "update"
CONF_LEADING_EDGE = "leading_edge"
CONF_WARMUP_BRIGHTNESS = "warmup_brightness"
# CONF_WARMUP_TIME = "warmup_time"
CONF_MIN_BRIGHTNESS = "min_brightness"
CONF_MAX_BRIGHTNESS = "max_brightness"
CONF_NRST_PIN = "nrst_pin"
CONF_BOOT0_PIN = "boot0_pin"
KNOWN_FIRMWARE = {
"51.5": (
"https://github.com/jamesturton/shelly-dimmer-stm32/releases/download/v51.5/shelly-dimmer-stm32_v51.5.bin",
"553fc1d78ed113227af7683eaa9c26189a961c4ea9a48000fb5aa8f8ac5d7b60",
),
"51.6": (
"https://github.com/jamesturton/shelly-dimmer-stm32/releases/download/v51.6/shelly-dimmer-stm32_v51.6.bin",
"eda483e111c914723a33f5088f1397d5c0b19333db4a88dc965636b976c16c36",
),
}
def parse_firmware_version(value):
match = re.match(r"(\d+).(\d+)", value)
if match is None:
raise ValueError(f"Not a valid version number {value}")
major = int(match[1])
minor = int(match[2])
return major, minor
def get_firmware(value):
if not value[CONF_UPDATE]:
return None
def dl(url):
try:
req = requests.get(url)
req.raise_for_status()
except requests.exceptions.RequestException as e:
raise cv.Invalid(f"Could not download firmware file ({url}): {e}")
h = hashlib.new("sha256")
h.update(req.content)
return req.content, h.hexdigest()
url = value[CONF_URL]
if CONF_SHA256 in value: # we have a hash, enable caching
path = (
Path(CORE.config_dir)
/ ".esphome"
/ DOMAIN
/ (value[CONF_SHA256] + "_fw_stm.bin")
)
if not path.is_file():
firmware_data, dl_hash = dl(url)
if dl_hash != value[CONF_SHA256]:
raise cv.Invalid(
f"Hash mismatch for {url}: {dl_hash} != {value[CONF_SHA256]}"
)
path.parent.mkdir(exist_ok=True, parents=True)
path.write_bytes(firmware_data)
else:
firmware_data = path.read_bytes()
else: # no caching, download every time
firmware_data, dl_hash = dl(url)
return [HexInt(x) for x in firmware_data]
def validate_firmware(value):
config = value.copy()
if CONF_URL not in config:
try:
config[CONF_URL], config[CONF_SHA256] = KNOWN_FIRMWARE[config[CONF_VERSION]]
except KeyError as e:
raise cv.Invalid(
f"Firmware {config[CONF_VERSION]} is unknown, please specify an '{CONF_URL}' ..."
) from e
get_firmware(config)
return config
def validate_sha256(value):
value = cv.string(value)
if not value.isalnum() or not len(value) == 64:
raise ValueError(f"Not a valid SHA256 hex string: {value}")
return value
def validate_version(value):
parse_firmware_version(value)
return value
CONFIG_SCHEMA = (
light.BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(ShellyDimmer),
cv.Optional(CONF_FIRMWARE, default="51.6"): cv.maybe_simple_value(
{
cv.Optional(CONF_URL): cv.url,
cv.Optional(CONF_SHA256): validate_sha256,
cv.Required(CONF_VERSION): validate_version,
cv.Optional(CONF_UPDATE, default=False): cv.boolean,
},
validate_firmware, # converts a simple version key to generate the full url
key=CONF_VERSION,
),
cv.Optional(CONF_NRST_PIN, default="GPIO5"): pins.gpio_output_pin_schema,
cv.Optional(CONF_BOOT0_PIN, default="GPIO4"): pins.gpio_output_pin_schema,
cv.Optional(CONF_LEADING_EDGE, default=False): cv.boolean,
cv.Optional(CONF_WARMUP_BRIGHTNESS, default=100): cv.uint16_t,
# cv.Optional(CONF_WARMUP_TIME, default=20): cv.uint16_t,
cv.Optional(CONF_MIN_BRIGHTNESS, default=0): cv.uint16_t,
cv.Optional(CONF_MAX_BRIGHTNESS, default=1000): cv.uint16_t,
cv.Optional(CONF_POWER): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_VOLTAGE,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
device_class=DEVICE_CLASS_POWER,
accuracy_decimals=2,
),
# Change the default gamma_correct setting.
cv.Optional(CONF_GAMMA_CORRECT, default=1.0): cv.positive_float,
}
)
.extend(cv.polling_component_schema("10s"))
.extend(uart.UART_DEVICE_SCHEMA)
)
def to_code(config):
fw_hex = get_firmware(config[CONF_FIRMWARE])
fw_major, fw_minor = parse_firmware_version(config[CONF_FIRMWARE][CONF_VERSION])
if fw_hex is not None:
cg.add_define("USE_SHD_FIRMWARE_DATA", fw_hex)
cg.add_define("USE_SHD_FIRMWARE_MAJOR_VERSION", fw_major)
cg.add_define("USE_SHD_FIRMWARE_MINOR_VERSION", fw_minor)
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
yield cg.register_component(var, config)
config.pop(
CONF_UPDATE_INTERVAL
) # drop UPDATE_INTERVAL as it does not apply to the light component
yield light.register_light(var, config)
yield uart.register_uart_device(var, config)
nrst_pin = yield cg.gpio_pin_expression(config[CONF_NRST_PIN])
cg.add(var.set_nrst_pin(nrst_pin))
boot0_pin = yield cg.gpio_pin_expression(config[CONF_BOOT0_PIN])
cg.add(var.set_boot0_pin(boot0_pin))
cg.add(var.set_leading_edge(config[CONF_LEADING_EDGE]))
cg.add(var.set_warmup_brightness(config[CONF_WARMUP_BRIGHTNESS]))
# cg.add(var.set_warmup_time(config[CONF_WARMUP_TIME]))
cg.add(var.set_min_brightness(config[CONF_MIN_BRIGHTNESS]))
cg.add(var.set_max_brightness(config[CONF_MAX_BRIGHTNESS]))
for key in [CONF_POWER, CONF_VOLTAGE, CONF_CURRENT]:
if key not in config:
continue
conf = config[key]
sens = yield sensor.new_sensor(conf)
cg.add(getattr(var, f"set_{key}_sensor")(sens))
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,117 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include "esphome/components/light/light_output.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
#include <array>
namespace esphome {
namespace shelly_dimmer {
class ShellyDimmer : public PollingComponent, public light::LightOutput, public uart::UARTDevice {
private:
static constexpr uint16_t SHELLY_DIMMER_BUFFER_SIZE = 256;
public:
float get_setup_priority() const override { return setup_priority::LATE; }
void setup() override;
void update() override;
void dump_config() override;
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS});
return traits;
}
void setup_state(light::LightState *state) override { this->state_ = state; }
void write_state(light::LightState *state) override;
void set_nrst_pin(GPIOPin *nrst_pin) { this->pin_nrst_ = nrst_pin; }
void set_boot0_pin(GPIOPin *boot0_pin) { this->pin_boot0_ = boot0_pin; }
void set_leading_edge(bool leading_edge) { this->leading_edge_ = leading_edge; }
void set_warmup_brightness(uint16_t warmup_brightness) { this->warmup_brightness_ = warmup_brightness; }
void set_warmup_time(uint16_t warmup_time) { this->warmup_time_ = warmup_time; }
void set_fade_rate(uint16_t fade_rate) { this->fade_rate_ = fade_rate; }
void set_min_brightness(uint16_t min_brightness) { this->min_brightness_ = min_brightness; }
void set_max_brightness(uint16_t max_brightness) { this->max_brightness_ = max_brightness; }
void set_power_sensor(sensor::Sensor *power_sensor) { this->power_sensor_ = power_sensor; }
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { this->voltage_sensor_ = voltage_sensor; }
void set_current_sensor(sensor::Sensor *current_sensor) { this->current_sensor_ = current_sensor; }
protected:
GPIOPin *pin_nrst_;
GPIOPin *pin_boot0_;
// Frame parser state.
uint8_t seq_{0};
std::array<uint8_t, SHELLY_DIMMER_BUFFER_SIZE> buffer_;
uint8_t buffer_pos_{0};
// Firmware version.
uint8_t version_major_;
uint8_t version_minor_;
// Configuration.
bool leading_edge_{false};
uint16_t warmup_brightness_{100};
uint16_t warmup_time_{20};
uint16_t fade_rate_{0};
uint16_t min_brightness_{0};
uint16_t max_brightness_{1000};
light::LightState *state_{nullptr};
sensor::Sensor *power_sensor_{nullptr};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
bool ready_{false};
uint16_t brightness_;
/// Convert relative brightness into a dimmer brightness value.
uint16_t convert_brightness_(float brightness);
/// Sends the given brightness value.
void send_brightness_(uint16_t brightness);
/// Sends dimmer configuration.
void send_settings_();
/// Performs a firmware upgrade.
bool upgrade_firmware_();
/// Sends a command and waits for an acknowledgement.
bool send_command_(uint8_t cmd, const uint8_t *payload, uint8_t len);
/// Frames a given command payload.
size_t frame_command_(uint8_t *data, uint8_t cmd, const uint8_t *payload, size_t len);
/// Handles a single byte as part of a protocol frame.
///
/// Returns -1 on failure, 0 when finished and 1 when more bytes needed.
int handle_byte_(uint8_t c);
/// Reads a response frame.
bool read_frame_();
/// Handles a complete frame.
bool handle_frame_();
/// Reset STM32 with the BOOT0 pin set to the given value.
void reset_(bool boot0);
/// Reset STM32 to boot the regular firmware.
void reset_normal_boot_();
/// Reset STM32 to boot into DFU mode to enable firmware upgrades.
void reset_dfu_boot_();
};
} // namespace shelly_dimmer
} // namespace esphome
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,129 @@
/*
stm32flash - Open Source ST STM32 flash program for Arduino
Copyright (C) 2010 Geoffrey McRae <geoff@spacevs.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_SHD_FIRMWARE_DATA
#include <cstdint>
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace shelly_dimmer {
/* flags */
constexpr auto STREAM_OPT_BYTE = (1 << 0); /* byte (not frame) oriented */
constexpr auto STREAM_OPT_GVR_ETX = (1 << 1); /* cmd GVR returns protection status */
constexpr auto STREAM_OPT_CMD_INIT = (1 << 2); /* use INIT cmd to autodetect speed */
constexpr auto STREAM_OPT_RETRY = (1 << 3); /* allowed read() retry after timeout */
constexpr auto STREAM_OPT_I2C = (1 << 4); /* i2c */
constexpr auto STREAM_OPT_STRETCH_W = (1 << 5); /* warning for no-stretching commands */
constexpr auto STREAM_SERIAL = (STREAM_OPT_BYTE | STREAM_OPT_GVR_ETX | STREAM_OPT_CMD_INIT | STREAM_OPT_RETRY);
constexpr auto STREAM_I2C = (STREAM_OPT_I2C | STREAM_OPT_STRETCH_W);
constexpr auto STM32_MAX_RX_FRAME = 256; /* cmd read memory */
constexpr auto STM32_MAX_TX_FRAME = (1 + 256 + 1); /* cmd write memory */
constexpr auto STM32_MAX_PAGES = 0x0000ffff;
constexpr auto STM32_MASS_ERASE = 0x00100000; /* > 2 x max_pages */
using stm32_err_t = enum Stm32Err {
STM32_ERR_OK = 0,
STM32_ERR_UNKNOWN, /* Generic error */
STM32_ERR_NACK,
STM32_ERR_NO_CMD, /* Command not available in bootloader */
};
using flags_t = enum Flags {
F_NO_ME = 1 << 0, /* Mass-Erase not supported */
F_OBLL = 1 << 1, /* OBL_LAUNCH required */
};
using stm32_cmd_t = struct Stm32Cmd {
uint8_t get;
uint8_t gvr;
uint8_t gid;
uint8_t rm;
uint8_t go;
uint8_t wm;
uint8_t er; /* this may be extended erase */
uint8_t wp;
uint8_t uw;
uint8_t rp;
uint8_t ur;
uint8_t crc;
};
using stm32_dev_t = struct Stm32Dev { // NOLINT
const uint16_t id;
const char *name;
const uint32_t ram_start, ram_end;
const uint32_t fl_start, fl_end;
const uint16_t fl_pps; // pages per sector
const uint32_t *fl_ps; // page size
const uint32_t opt_start, opt_end;
const uint32_t mem_start, mem_end;
const uint32_t flags;
};
using stm32_t = struct Stm32 {
uart::UARTDevice *stream;
uint8_t flags;
struct VarlenCmd *cmd_get_reply;
uint8_t bl_version;
uint8_t version;
uint8_t option1, option2;
uint16_t pid;
stm32_cmd_t *cmd;
const stm32_dev_t *dev;
};
/*
* Specify the length of reply for command GET
* This is helpful for frame-oriented protocols, e.g. i2c, to avoid time
* consuming try-fail-timeout-retry operation.
* On byte-oriented protocols, i.e. UART, this information would be skipped
* after read the first byte, so not needed.
*/
struct VarlenCmd {
uint8_t version;
uint8_t length;
};
stm32_t *stm32_init(uart::UARTDevice *stream, uint8_t flags, char init);
void stm32_close(stm32_t *stm);
stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, uint8_t *data, unsigned int len);
stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, const uint8_t *data, unsigned int len);
stm32_err_t stm32_wunprot_memory(const stm32_t *stm);
stm32_err_t stm32_wprot_memory(const stm32_t *stm);
stm32_err_t stm32_erase_memory(const stm32_t *stm, uint32_t spage, uint32_t pages);
stm32_err_t stm32_go(const stm32_t *stm, uint32_t address);
stm32_err_t stm32_reset_device(const stm32_t *stm);
stm32_err_t stm32_readprot_memory(const stm32_t *stm);
stm32_err_t stm32_runprot_memory(const stm32_t *stm);
stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, uint32_t length, uint32_t *crc);
stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, uint32_t length, uint32_t *crc);
uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len);
} // namespace shelly_dimmer
} // namespace esphome
#endif // USE_SHD_FIRMWARE_DATA
+6
View File
@@ -93,3 +93,9 @@
//#define USE_BSEC // Requires a library with proprietary license.
#define USE_DASHBOARD_IMPORT
// Dummy firmware payload for shelly_dimmer
#define USE_SHD_FIRMWARE_MAJOR_VERSION 56
#define USE_SHD_FIRMWARE_MINOR_VERSION 5
#define USE_SHD_FIRMWARE_DATA \
{}
+12
View File
@@ -1751,6 +1751,18 @@ light:
to: 25
- single_light_id: ${roomname}_lights
- platform: shelly_dimmer
name: "Shelly Dimmer Light"
power:
name: "Shelly Dimmer Power"
voltage:
name: "Shelly Dimmer Voltage"
current:
name: "Shelly Dimmer Current"
max_brightness: 500
firmware: "51.6"
uart_id: uart0
remote_transmitter:
- pin: 32
carrier_duty_percent: 100%