esphome 2025.2.2__py3-none-any.whl → 2025.3.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- esphome/__main__.py +9 -1
- esphome/components/api/api_connection.cpp +426 -70
- esphome/components/api/api_connection.h +117 -25
- esphome/components/api/api_pb2.cpp +9 -0
- esphome/components/api/api_pb2.h +1 -0
- esphome/components/api/api_server.cpp +2 -2
- esphome/components/api/list_entities.cpp +76 -22
- esphome/components/api/list_entities.h +1 -0
- esphome/components/api/subscribe_state.h +2 -0
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -0
- esphome/components/bmp085/bmp085.cpp +1 -1
- esphome/components/chsc6x/__init__.py +2 -0
- esphome/components/chsc6x/chsc6x_touchscreen.cpp +47 -0
- esphome/components/chsc6x/chsc6x_touchscreen.h +34 -0
- esphome/components/chsc6x/touchscreen.py +33 -0
- esphome/components/climate/__init__.py +0 -1
- esphome/components/cst816/binary_sensor/__init__.py +2 -25
- esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +3 -14
- esphome/components/cst816/touchscreen/cst816_touchscreen.h +0 -4
- esphome/components/esp32_ble_beacon/__init__.py +3 -1
- esphome/components/esp8266/gpio.py +1 -2
- esphome/components/font/__init__.py +185 -185
- esphome/components/font/font.cpp +4 -4
- esphome/components/font/font.h +1 -0
- esphome/components/haier/climate.py +11 -10
- esphome/components/hbridge/switch/hbridge_switch.cpp +2 -2
- esphome/components/heatpumpir/climate.py +2 -1
- esphome/components/heatpumpir/heatpumpir.cpp +1 -0
- esphome/components/heatpumpir/heatpumpir.h +1 -0
- esphome/components/i2c/__init__.py +6 -6
- esphome/components/i2c/i2c_bus_esp_idf.cpp +6 -2
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
- esphome/components/ili9xxx/display.py +1 -0
- esphome/components/ili9xxx/ili9xxx_display.h +5 -0
- esphome/components/ili9xxx/ili9xxx_init.h +59 -0
- esphome/components/ld2450/__init__.py +51 -0
- esphome/components/ld2450/binary_sensor.py +47 -0
- esphome/components/ld2450/button/__init__.py +45 -0
- esphome/components/ld2450/button/reset_button.cpp +9 -0
- esphome/components/ld2450/button/reset_button.h +18 -0
- esphome/components/ld2450/button/restart_button.cpp +9 -0
- esphome/components/ld2450/button/restart_button.h +18 -0
- esphome/components/ld2450/ld2450.cpp +876 -0
- esphome/components/ld2450/ld2450.h +234 -0
- esphome/components/ld2450/number/__init__.py +121 -0
- esphome/components/ld2450/number/presence_timeout_number.cpp +12 -0
- esphome/components/ld2450/number/presence_timeout_number.h +18 -0
- esphome/components/ld2450/number/zone_coordinate_number.cpp +14 -0
- esphome/components/ld2450/number/zone_coordinate_number.h +19 -0
- esphome/components/ld2450/select/__init__.py +56 -0
- esphome/components/ld2450/select/baud_rate_select.cpp +12 -0
- esphome/components/ld2450/select/baud_rate_select.h +18 -0
- esphome/components/ld2450/select/zone_type_select.cpp +12 -0
- esphome/components/ld2450/select/zone_type_select.h +18 -0
- esphome/components/ld2450/sensor.py +156 -0
- esphome/components/ld2450/switch/__init__.py +45 -0
- esphome/components/ld2450/switch/bluetooth_switch.cpp +12 -0
- esphome/components/ld2450/switch/bluetooth_switch.h +18 -0
- esphome/components/ld2450/switch/multi_target_switch.cpp +12 -0
- esphome/components/ld2450/switch/multi_target_switch.h +18 -0
- esphome/components/ld2450/text_sensor.py +62 -0
- esphome/components/lvgl/defines.py +0 -2
- esphome/components/lvgl/font.cpp +1 -1
- esphome/components/lvgl/lvgl_esphome.cpp +27 -19
- esphome/components/lvgl/widgets/img.py +1 -3
- esphome/components/mcp2515/mcp2515.cpp +1 -0
- esphome/components/mlx90393/sensor.py +53 -33
- esphome/components/mlx90393/sensor_mlx90393.cpp +4 -0
- esphome/components/mlx90393/sensor_mlx90393.h +8 -3
- esphome/components/mqtt/__init__.py +2 -2
- esphome/components/msa3xx/__init__.py +189 -0
- esphome/components/msa3xx/binary_sensor.py +40 -0
- esphome/components/msa3xx/msa3xx.cpp +417 -0
- esphome/components/msa3xx/msa3xx.h +311 -0
- esphome/components/msa3xx/sensor.py +42 -0
- esphome/components/msa3xx/text_sensor.py +38 -0
- esphome/components/nfc/binary_sensor/__init__.py +4 -4
- esphome/components/opentherm/binary_sensor/__init__.py +4 -4
- esphome/components/opentherm/generate.py +6 -6
- esphome/components/opentherm/sensor/__init__.py +5 -6
- esphome/components/packages/__init__.py +35 -11
- esphome/components/pn532/binary_sensor.py +4 -4
- esphome/components/rc522/binary_sensor.py +4 -4
- esphome/components/socket/bsd_sockets_impl.cpp +1 -0
- esphome/components/socket/lwip_sockets_impl.cpp +1 -0
- esphome/components/socket/socket.h +3 -1
- esphome/components/ssd1306_base/__init__.py +7 -7
- esphome/components/thermostat/climate.py +1 -1
- esphome/components/tmp1075/tmp1075.cpp +7 -11
- esphome/components/tmp1075/tmp1075.h +1 -2
- esphome/components/tormatic/__init__.py +1 -0
- esphome/components/tormatic/cover.py +47 -0
- esphome/components/tormatic/tormatic_cover.cpp +355 -0
- esphome/components/tormatic/tormatic_cover.h +60 -0
- esphome/components/tormatic/tormatic_protocol.h +211 -0
- esphome/components/touchscreen/binary_sensor/__init__.py +3 -0
- esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.cpp +7 -1
- esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h +3 -1
- esphome/components/touchscreen/touchscreen.cpp +3 -4
- esphome/components/udp/udp_component.h +4 -1
- esphome/components/web_server/list_entities.cpp +70 -66
- esphome/components/web_server/list_entities.h +43 -22
- esphome/components/web_server/web_server.cpp +345 -68
- esphome/components/web_server/web_server.h +138 -6
- esphome/components/web_server_base/__init__.py +1 -1
- esphome/components/web_server_idf/__init__.py +2 -0
- esphome/components/web_server_idf/web_server_idf.cpp +177 -30
- esphome/components/web_server_idf/web_server_idf.h +53 -4
- esphome/config_validation.py +23 -125
- esphome/const.py +5 -1
- esphome/core/config.py +12 -4
- esphome/core/defines.h +1 -1
- esphome/core/helpers.h +5 -3
- esphome/core/time.cpp +1 -0
- esphome/cpp_generator.py +3 -3
- esphome/dashboard/core.py +30 -21
- esphome/dashboard/dns.py +7 -1
- esphome/dashboard/entries.py +83 -16
- esphome/dashboard/settings.py +0 -4
- esphome/dashboard/status/mdns.py +43 -14
- esphome/dashboard/status/mqtt.py +22 -9
- esphome/dashboard/status/ping.py +54 -10
- esphome/dashboard/web_server.py +56 -24
- esphome/storage_json.py +4 -0
- esphome/wizard.py +13 -17
- esphome/writer.py +1 -3
- esphome/yaml_util.py +36 -33
- esphome/zeroconf.py +9 -21
- {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/METADATA +5 -5
- {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/RECORD +134 -94
- esphome/components/cst816/binary_sensor/cst816_button.h +0 -27
- {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/LICENSE +0 -0
- {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/WHEEL +0 -0
- {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/entry_points.txt +0 -0
- {esphome-2025.2.2.dist-info → esphome-2025.3.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
from esphome import automation
|
2
|
+
import esphome.codegen as cg
|
3
|
+
from esphome.components import i2c
|
4
|
+
import esphome.config_validation as cv
|
5
|
+
from esphome.const import (
|
6
|
+
CONF_CALIBRATION,
|
7
|
+
CONF_ID,
|
8
|
+
CONF_MIRROR_X,
|
9
|
+
CONF_MIRROR_Y,
|
10
|
+
CONF_OFFSET_X,
|
11
|
+
CONF_OFFSET_Y,
|
12
|
+
CONF_OFFSET_Z,
|
13
|
+
CONF_RANGE,
|
14
|
+
CONF_RESOLUTION,
|
15
|
+
CONF_SWAP_XY,
|
16
|
+
CONF_TRANSFORM,
|
17
|
+
CONF_TYPE,
|
18
|
+
)
|
19
|
+
|
20
|
+
CODEOWNERS = ["@latonita"]
|
21
|
+
DEPENDENCIES = ["i2c"]
|
22
|
+
|
23
|
+
MULTI_CONF = True
|
24
|
+
|
25
|
+
CONF_MSA3XX_ID = "msa3xx_id"
|
26
|
+
|
27
|
+
CONF_MIRROR_Z = "mirror_z"
|
28
|
+
CONF_ON_ACTIVE = "on_active"
|
29
|
+
CONF_ON_DOUBLE_TAP = "on_double_tap"
|
30
|
+
CONF_ON_FREEFALL = "on_freefall"
|
31
|
+
CONF_ON_ORIENTATION = "on_orientation"
|
32
|
+
CONF_ON_TAP = "on_tap"
|
33
|
+
|
34
|
+
MODEL_MSA301 = "MSA301"
|
35
|
+
MODEL_MSA311 = "MSA311"
|
36
|
+
|
37
|
+
msa3xx_ns = cg.esphome_ns.namespace("msa3xx")
|
38
|
+
MSA3xxComponent = msa3xx_ns.class_(
|
39
|
+
"MSA3xxComponent", cg.PollingComponent, i2c.I2CDevice
|
40
|
+
)
|
41
|
+
|
42
|
+
MSAModels = msa3xx_ns.enum("Model", True)
|
43
|
+
MSA_MODELS = {
|
44
|
+
MODEL_MSA301: MSAModels.MSA301,
|
45
|
+
MODEL_MSA311: MSAModels.MSA311,
|
46
|
+
}
|
47
|
+
|
48
|
+
MSARange = msa3xx_ns.enum("Range", True)
|
49
|
+
MSA_RANGES = {
|
50
|
+
"2G": MSARange.RANGE_2G,
|
51
|
+
"4G": MSARange.RANGE_4G,
|
52
|
+
"8G": MSARange.RANGE_8G,
|
53
|
+
"16G": MSARange.RANGE_16G,
|
54
|
+
}
|
55
|
+
|
56
|
+
MSAResolution = msa3xx_ns.enum("Resolution", True)
|
57
|
+
RESOLUTIONS_MSA301 = {
|
58
|
+
14: MSAResolution.RES_14BIT,
|
59
|
+
12: MSAResolution.RES_12BIT,
|
60
|
+
10: MSAResolution.RES_10BIT,
|
61
|
+
8: MSAResolution.RES_8BIT,
|
62
|
+
}
|
63
|
+
|
64
|
+
RESOLUTIONS_MSA311 = {
|
65
|
+
12: MSAResolution.RES_12BIT,
|
66
|
+
}
|
67
|
+
|
68
|
+
_COMMON_SCHEMA = cv.Schema(
|
69
|
+
{
|
70
|
+
cv.GenerateID(): cv.declare_id(MSA3xxComponent),
|
71
|
+
cv.Optional(CONF_RANGE, default="2G"): cv.enum(MSA_RANGES, upper=True),
|
72
|
+
cv.Optional(CONF_CALIBRATION): cv.Schema(
|
73
|
+
{
|
74
|
+
cv.Optional(CONF_OFFSET_X, default=0): cv.float_range(
|
75
|
+
min=-4.5, max=4.5
|
76
|
+
),
|
77
|
+
cv.Optional(CONF_OFFSET_Y, default=0): cv.float_range(
|
78
|
+
min=-4.5, max=4.5
|
79
|
+
),
|
80
|
+
cv.Optional(CONF_OFFSET_Z, default=0): cv.float_range(
|
81
|
+
min=-4.5, max=4.5
|
82
|
+
),
|
83
|
+
}
|
84
|
+
),
|
85
|
+
cv.Optional(CONF_TRANSFORM): cv.Schema(
|
86
|
+
{
|
87
|
+
cv.Optional(CONF_MIRROR_X, default=False): cv.boolean,
|
88
|
+
cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean,
|
89
|
+
cv.Optional(CONF_MIRROR_Z, default=False): cv.boolean,
|
90
|
+
cv.Optional(CONF_SWAP_XY, default=False): cv.boolean,
|
91
|
+
}
|
92
|
+
),
|
93
|
+
cv.Optional(CONF_ON_ACTIVE): automation.validate_automation(single=True),
|
94
|
+
cv.Optional(CONF_ON_TAP): automation.validate_automation(single=True),
|
95
|
+
cv.Optional(CONF_ON_DOUBLE_TAP): automation.validate_automation(single=True),
|
96
|
+
cv.Optional(CONF_ON_FREEFALL): automation.validate_automation(single=True),
|
97
|
+
cv.Optional(CONF_ON_ORIENTATION): automation.validate_automation(single=True),
|
98
|
+
}
|
99
|
+
).extend(cv.polling_component_schema("10s"))
|
100
|
+
|
101
|
+
|
102
|
+
CONFIG_SCHEMA = cv.typed_schema(
|
103
|
+
{
|
104
|
+
MODEL_MSA301: _COMMON_SCHEMA.extend(
|
105
|
+
{
|
106
|
+
cv.Optional(CONF_RESOLUTION, default=14): cv.enum(RESOLUTIONS_MSA301),
|
107
|
+
}
|
108
|
+
).extend(i2c.i2c_device_schema(0x26)),
|
109
|
+
MODEL_MSA311: _COMMON_SCHEMA.extend(
|
110
|
+
{
|
111
|
+
cv.Optional(CONF_RESOLUTION, default=12): cv.enum(RESOLUTIONS_MSA311),
|
112
|
+
}
|
113
|
+
).extend(i2c.i2c_device_schema(0x62)),
|
114
|
+
},
|
115
|
+
upper=True,
|
116
|
+
enum=MSA_MODELS,
|
117
|
+
)
|
118
|
+
|
119
|
+
MSA_SENSOR_SCHEMA = cv.Schema(
|
120
|
+
{
|
121
|
+
cv.GenerateID(CONF_MSA3XX_ID): cv.use_id(MSA3xxComponent),
|
122
|
+
}
|
123
|
+
)
|
124
|
+
|
125
|
+
|
126
|
+
async def to_code(config):
|
127
|
+
var = cg.new_Pvariable(config[CONF_ID])
|
128
|
+
await cg.register_component(var, config)
|
129
|
+
await i2c.register_i2c_device(var, config)
|
130
|
+
|
131
|
+
cg.add(var.set_model(config[CONF_TYPE]))
|
132
|
+
cg.add(var.set_range(MSA_RANGES[config[CONF_RANGE]]))
|
133
|
+
cg.add(var.set_resolution(RESOLUTIONS_MSA301[config[CONF_RESOLUTION]]))
|
134
|
+
|
135
|
+
if transform := config.get(CONF_TRANSFORM):
|
136
|
+
cg.add(
|
137
|
+
var.set_transform(
|
138
|
+
transform[CONF_MIRROR_X],
|
139
|
+
transform[CONF_MIRROR_Y],
|
140
|
+
transform[CONF_MIRROR_Z],
|
141
|
+
transform[CONF_SWAP_XY],
|
142
|
+
)
|
143
|
+
)
|
144
|
+
|
145
|
+
if calibration_config := config.get(CONF_CALIBRATION):
|
146
|
+
cg.add(
|
147
|
+
var.set_offset(
|
148
|
+
calibration_config[CONF_OFFSET_X],
|
149
|
+
calibration_config[CONF_OFFSET_Y],
|
150
|
+
calibration_config[CONF_OFFSET_Z],
|
151
|
+
)
|
152
|
+
)
|
153
|
+
|
154
|
+
# Triggers secton
|
155
|
+
|
156
|
+
if CONF_ON_ORIENTATION in config:
|
157
|
+
await automation.build_automation(
|
158
|
+
var.get_orientation_trigger(),
|
159
|
+
[],
|
160
|
+
config[CONF_ON_ORIENTATION],
|
161
|
+
)
|
162
|
+
|
163
|
+
if CONF_ON_TAP in config:
|
164
|
+
await automation.build_automation(
|
165
|
+
var.get_tap_trigger(),
|
166
|
+
[],
|
167
|
+
config[CONF_ON_TAP],
|
168
|
+
)
|
169
|
+
|
170
|
+
if CONF_ON_DOUBLE_TAP in config:
|
171
|
+
await automation.build_automation(
|
172
|
+
var.get_double_tap_trigger(),
|
173
|
+
[],
|
174
|
+
config[CONF_ON_DOUBLE_TAP],
|
175
|
+
)
|
176
|
+
|
177
|
+
if CONF_ON_ACTIVE in config:
|
178
|
+
await automation.build_automation(
|
179
|
+
var.get_active_trigger(),
|
180
|
+
[],
|
181
|
+
config[CONF_ON_ACTIVE],
|
182
|
+
)
|
183
|
+
|
184
|
+
if CONF_ON_FREEFALL in config:
|
185
|
+
await automation.build_automation(
|
186
|
+
var.get_freefall_trigger(),
|
187
|
+
[],
|
188
|
+
config[CONF_ON_FREEFALL],
|
189
|
+
)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import esphome.codegen as cg
|
2
|
+
from esphome.components import binary_sensor
|
3
|
+
import esphome.config_validation as cv
|
4
|
+
from esphome.const import CONF_ACTIVE, CONF_NAME, DEVICE_CLASS_VIBRATION, ICON_VIBRATE
|
5
|
+
|
6
|
+
from . import CONF_MSA3XX_ID, MSA_SENSOR_SCHEMA
|
7
|
+
|
8
|
+
CODEOWNERS = ["@latonita"]
|
9
|
+
DEPENDENCIES = ["msa3xx"]
|
10
|
+
|
11
|
+
CONF_TAP = "tap"
|
12
|
+
CONF_DOUBLE_TAP = "double_tap"
|
13
|
+
|
14
|
+
ICON_TAP = "mdi:gesture-tap"
|
15
|
+
ICON_DOUBLE_TAP = "mdi:gesture-double-tap"
|
16
|
+
|
17
|
+
EVENT_SENSORS = (CONF_TAP, CONF_DOUBLE_TAP, CONF_ACTIVE)
|
18
|
+
ICONS = (ICON_TAP, ICON_DOUBLE_TAP, ICON_VIBRATE)
|
19
|
+
|
20
|
+
CONFIG_SCHEMA = MSA_SENSOR_SCHEMA.extend(
|
21
|
+
{
|
22
|
+
cv.Optional(event): cv.maybe_simple_value(
|
23
|
+
binary_sensor.binary_sensor_schema(
|
24
|
+
device_class=DEVICE_CLASS_VIBRATION,
|
25
|
+
icon=icon,
|
26
|
+
),
|
27
|
+
key=CONF_NAME,
|
28
|
+
)
|
29
|
+
for event, icon in zip(EVENT_SENSORS, ICONS)
|
30
|
+
}
|
31
|
+
)
|
32
|
+
|
33
|
+
|
34
|
+
async def to_code(config):
|
35
|
+
hub = await cg.get_variable(config[CONF_MSA3XX_ID])
|
36
|
+
|
37
|
+
for sensor in EVENT_SENSORS:
|
38
|
+
if sensor in config:
|
39
|
+
sens = await binary_sensor.new_binary_sensor(config[sensor])
|
40
|
+
cg.add(getattr(hub, f"set_{sensor}_binary_sensor")(sens))
|
@@ -0,0 +1,417 @@
|
|
1
|
+
#include "msa3xx.h"
|
2
|
+
#include "esphome/core/log.h"
|
3
|
+
#include "esphome/core/hal.h"
|
4
|
+
#include "esphome/core/helpers.h"
|
5
|
+
|
6
|
+
namespace esphome {
|
7
|
+
namespace msa3xx {
|
8
|
+
|
9
|
+
static const char *const TAG = "msa3xx";
|
10
|
+
|
11
|
+
const uint8_t MSA_3XX_PART_ID = 0x13;
|
12
|
+
|
13
|
+
const float GRAVITY_EARTH = 9.80665f;
|
14
|
+
const float LSB_COEFF = 1000.0f / (GRAVITY_EARTH * 3.9); // LSB to 1 LSB = 3.9mg = 0.0039g
|
15
|
+
const float G_OFFSET_MIN = -4.5f; // -127...127 LSB = +- 0.4953g = +- 4.857 m/s^2 => +- 4.5 for the safe
|
16
|
+
const float G_OFFSET_MAX = 4.5f; // -127...127 LSB = +- 0.4953g = +- 4.857 m/s^2 => +- 4.5 for the safe
|
17
|
+
|
18
|
+
const uint8_t RESOLUTION[] = {14, 12, 10, 8};
|
19
|
+
|
20
|
+
const uint32_t TAP_COOLDOWN_MS = 500;
|
21
|
+
const uint32_t DOUBLE_TAP_COOLDOWN_MS = 500;
|
22
|
+
const uint32_t ACTIVITY_COOLDOWN_MS = 500;
|
23
|
+
|
24
|
+
const char *model_to_string(Model model) {
|
25
|
+
switch (model) {
|
26
|
+
case Model::MSA301:
|
27
|
+
return "MSA301";
|
28
|
+
case Model::MSA311:
|
29
|
+
return "MSA311";
|
30
|
+
default:
|
31
|
+
return "Unknown";
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
const char *power_mode_to_string(PowerMode power_mode) {
|
36
|
+
switch (power_mode) {
|
37
|
+
case PowerMode::NORMAL:
|
38
|
+
return "Normal";
|
39
|
+
case PowerMode::LOW_POWER:
|
40
|
+
return "Low Power";
|
41
|
+
case PowerMode::SUSPEND:
|
42
|
+
return "Suspend";
|
43
|
+
default:
|
44
|
+
return "Unknown";
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
const char *res_to_string(Resolution resolution) {
|
49
|
+
switch (resolution) {
|
50
|
+
case Resolution::RES_14BIT:
|
51
|
+
return "14-bit";
|
52
|
+
case Resolution::RES_12BIT:
|
53
|
+
return "12-bit";
|
54
|
+
case Resolution::RES_10BIT:
|
55
|
+
return "10-bit";
|
56
|
+
case Resolution::RES_8BIT:
|
57
|
+
return "8-bit";
|
58
|
+
default:
|
59
|
+
return "Unknown";
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
const char *range_to_string(Range range) {
|
64
|
+
switch (range) {
|
65
|
+
case Range::RANGE_2G:
|
66
|
+
return "±2g";
|
67
|
+
case Range::RANGE_4G:
|
68
|
+
return "±4g";
|
69
|
+
case Range::RANGE_8G:
|
70
|
+
return "±8g";
|
71
|
+
case Range::RANGE_16G:
|
72
|
+
return "±16g";
|
73
|
+
default:
|
74
|
+
return "Unknown";
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
const char *bandwidth_to_string(Bandwidth bandwidth) {
|
79
|
+
switch (bandwidth) {
|
80
|
+
case Bandwidth::BW_1_95HZ:
|
81
|
+
return "1.95 Hz";
|
82
|
+
case Bandwidth::BW_3_9HZ:
|
83
|
+
return "3.9 Hz";
|
84
|
+
case Bandwidth::BW_7_81HZ:
|
85
|
+
return "7.81 Hz";
|
86
|
+
case Bandwidth::BW_15_63HZ:
|
87
|
+
return "15.63 Hz";
|
88
|
+
case Bandwidth::BW_31_25HZ:
|
89
|
+
return "31.25 Hz";
|
90
|
+
case Bandwidth::BW_62_5HZ:
|
91
|
+
return "62.5 Hz";
|
92
|
+
case Bandwidth::BW_125HZ:
|
93
|
+
return "125 Hz";
|
94
|
+
case Bandwidth::BW_250HZ:
|
95
|
+
return "250 Hz";
|
96
|
+
case Bandwidth::BW_500HZ:
|
97
|
+
return "500 Hz";
|
98
|
+
default:
|
99
|
+
return "Unknown";
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
const char *orientation_xy_to_string(OrientationXY orientation) {
|
104
|
+
switch (orientation) {
|
105
|
+
case OrientationXY::PORTRAIT_UPRIGHT:
|
106
|
+
return "Portrait Upright";
|
107
|
+
case OrientationXY::PORTRAIT_UPSIDE_DOWN:
|
108
|
+
return "Portrait Upside Down";
|
109
|
+
case OrientationXY::LANDSCAPE_LEFT:
|
110
|
+
return "Landscape Left";
|
111
|
+
case OrientationXY::LANDSCAPE_RIGHT:
|
112
|
+
return "Landscape Right";
|
113
|
+
default:
|
114
|
+
return "Unknown";
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
const char *orientation_z_to_string(bool orientation) { return orientation ? "Downwards looking" : "Upwards looking"; }
|
119
|
+
|
120
|
+
void MSA3xxComponent::setup() {
|
121
|
+
ESP_LOGCONFIG(TAG, "Setting up MSA3xx...");
|
122
|
+
|
123
|
+
uint8_t part_id{0xff};
|
124
|
+
if (!this->read_byte(static_cast<uint8_t>(RegisterMap::PART_ID), &part_id) || (part_id != MSA_3XX_PART_ID)) {
|
125
|
+
ESP_LOGE(TAG, "Part ID is wrong or missing. Got 0x%02X", part_id);
|
126
|
+
this->mark_failed();
|
127
|
+
return;
|
128
|
+
}
|
129
|
+
|
130
|
+
// Resolution LSB/g
|
131
|
+
// Range : MSA301 : MSA311
|
132
|
+
// S2g : 1024 (2^10) : 4096 (2^12)
|
133
|
+
// S4g : 512 (2^9) : 2048 (2^11)
|
134
|
+
// S8g : 256 (2^8) : 1024 (2^10)
|
135
|
+
// S16g : 128 (2^7) : 512 (2^9)
|
136
|
+
if (this->model_ == Model::MSA301) {
|
137
|
+
this->device_params_.accel_data_width = 14;
|
138
|
+
this->device_params_.scale_factor_exp = static_cast<uint8_t>(this->range_) - 12;
|
139
|
+
} else if (this->model_ == Model::MSA311) {
|
140
|
+
this->device_params_.accel_data_width = 12;
|
141
|
+
this->device_params_.scale_factor_exp = static_cast<uint8_t>(this->range_) - 10;
|
142
|
+
} else {
|
143
|
+
ESP_LOGE(TAG, "Unknown model");
|
144
|
+
this->mark_failed();
|
145
|
+
return;
|
146
|
+
}
|
147
|
+
|
148
|
+
this->setup_odr_(this->data_rate_);
|
149
|
+
this->setup_power_mode_bandwidth_(this->power_mode_, this->bandwidth_);
|
150
|
+
this->setup_range_resolution_(this->range_, this->resolution_); // 2g...16g, 14...8 bit
|
151
|
+
this->setup_offset_(this->offset_x_, this->offset_y_, this->offset_z_); // calibration offsets
|
152
|
+
this->write_byte(static_cast<uint8_t>(RegisterMap::TAP_DURATION), 0b11000100); // set tap duration 250ms
|
153
|
+
this->write_byte(static_cast<uint8_t>(RegisterMap::SWAP_POLARITY), this->swap_.raw); // set axes polarity
|
154
|
+
this->write_byte(static_cast<uint8_t>(RegisterMap::INT_SET_0), 0b01110111); // enable all interrupts
|
155
|
+
this->write_byte(static_cast<uint8_t>(RegisterMap::INT_SET_1), 0b00011000); // including orientation
|
156
|
+
}
|
157
|
+
|
158
|
+
void MSA3xxComponent::dump_config() {
|
159
|
+
ESP_LOGCONFIG(TAG, "MSA3xx:");
|
160
|
+
LOG_I2C_DEVICE(this);
|
161
|
+
if (this->is_failed()) {
|
162
|
+
ESP_LOGE(TAG, "Communication with MSA3xx failed!");
|
163
|
+
}
|
164
|
+
ESP_LOGCONFIG(TAG, " Model: %s", model_to_string(this->model_));
|
165
|
+
ESP_LOGCONFIG(TAG, " Power Mode: %s", power_mode_to_string(this->power_mode_));
|
166
|
+
ESP_LOGCONFIG(TAG, " Bandwidth: %s", bandwidth_to_string(this->bandwidth_));
|
167
|
+
ESP_LOGCONFIG(TAG, " Range: %s", range_to_string(this->range_));
|
168
|
+
ESP_LOGCONFIG(TAG, " Resolution: %s", res_to_string(this->resolution_));
|
169
|
+
ESP_LOGCONFIG(TAG, " Offsets: {%.3f m/s², %.3f m/s², %.3f m/s²}", this->offset_x_, this->offset_y_, this->offset_z_);
|
170
|
+
ESP_LOGCONFIG(TAG, " Transform: {mirror_x=%s, mirror_y=%s, mirror_z=%s, swap_xy=%s}", YESNO(this->swap_.x_polarity),
|
171
|
+
YESNO(this->swap_.y_polarity), YESNO(this->swap_.z_polarity), YESNO(this->swap_.x_y_swap));
|
172
|
+
LOG_UPDATE_INTERVAL(this);
|
173
|
+
|
174
|
+
#ifdef USE_BINARY_SENSOR
|
175
|
+
LOG_BINARY_SENSOR(" ", "Tap", this->tap_binary_sensor_);
|
176
|
+
LOG_BINARY_SENSOR(" ", "Double Tap", this->double_tap_binary_sensor_);
|
177
|
+
LOG_BINARY_SENSOR(" ", "Active", this->active_binary_sensor_);
|
178
|
+
#endif
|
179
|
+
|
180
|
+
#ifdef USE_SENSOR
|
181
|
+
LOG_SENSOR(" ", "Acceleration X", this->acceleration_x_sensor_);
|
182
|
+
LOG_SENSOR(" ", "Acceleration Y", this->acceleration_y_sensor_);
|
183
|
+
LOG_SENSOR(" ", "Acceleration Z", this->acceleration_z_sensor_);
|
184
|
+
#endif
|
185
|
+
|
186
|
+
#ifdef USE_TEXT_SENSOR
|
187
|
+
LOG_TEXT_SENSOR(" ", "Orientation XY", this->orientation_xy_text_sensor_);
|
188
|
+
LOG_TEXT_SENSOR(" ", "Orientation Z", this->orientation_z_text_sensor_);
|
189
|
+
#endif
|
190
|
+
}
|
191
|
+
|
192
|
+
bool MSA3xxComponent::read_data_() {
|
193
|
+
uint8_t accel_data[6];
|
194
|
+
if (!this->read_bytes(static_cast<uint8_t>(RegisterMap::ACC_X_LSB), accel_data, 6)) {
|
195
|
+
return false;
|
196
|
+
}
|
197
|
+
|
198
|
+
auto raw_to_x_bit = [](uint16_t lsb, uint16_t msb, uint8_t data_bits) -> uint16_t {
|
199
|
+
return ((msb << 8) | lsb) >> (16 - data_bits);
|
200
|
+
};
|
201
|
+
|
202
|
+
auto lpf = [](float new_value, float old_value, float alpha = 0.5f) {
|
203
|
+
return alpha * new_value + (1.0f - alpha) * old_value;
|
204
|
+
};
|
205
|
+
|
206
|
+
this->data_.lsb_x =
|
207
|
+
this->twos_complement_(raw_to_x_bit(accel_data[0], accel_data[1], this->device_params_.accel_data_width),
|
208
|
+
this->device_params_.accel_data_width);
|
209
|
+
this->data_.lsb_y =
|
210
|
+
this->twos_complement_(raw_to_x_bit(accel_data[2], accel_data[3], this->device_params_.accel_data_width),
|
211
|
+
this->device_params_.accel_data_width);
|
212
|
+
this->data_.lsb_z =
|
213
|
+
this->twos_complement_(raw_to_x_bit(accel_data[4], accel_data[5], this->device_params_.accel_data_width),
|
214
|
+
this->device_params_.accel_data_width);
|
215
|
+
|
216
|
+
this->data_.x = lpf(ldexp(this->data_.lsb_x, this->device_params_.scale_factor_exp) * GRAVITY_EARTH, this->data_.x);
|
217
|
+
this->data_.y = lpf(ldexp(this->data_.lsb_y, this->device_params_.scale_factor_exp) * GRAVITY_EARTH, this->data_.y);
|
218
|
+
this->data_.z = lpf(ldexp(this->data_.lsb_z, this->device_params_.scale_factor_exp) * GRAVITY_EARTH, this->data_.z);
|
219
|
+
|
220
|
+
return true;
|
221
|
+
}
|
222
|
+
|
223
|
+
bool MSA3xxComponent::read_motion_status_() {
|
224
|
+
if (!this->read_byte(static_cast<uint8_t>(RegisterMap::MOTION_INTERRUPT), &this->status_.motion_int.raw)) {
|
225
|
+
return false;
|
226
|
+
}
|
227
|
+
|
228
|
+
if (!this->read_byte(static_cast<uint8_t>(RegisterMap::ORIENTATION_STATUS), &this->status_.orientation.raw)) {
|
229
|
+
return false;
|
230
|
+
}
|
231
|
+
|
232
|
+
return true;
|
233
|
+
}
|
234
|
+
|
235
|
+
void MSA3xxComponent::loop() {
|
236
|
+
if (!this->is_ready()) {
|
237
|
+
return;
|
238
|
+
}
|
239
|
+
|
240
|
+
RegMotionInterrupt old_motion_int = this->status_.motion_int;
|
241
|
+
|
242
|
+
if (!this->read_data_() || !this->read_motion_status_()) {
|
243
|
+
this->status_set_warning();
|
244
|
+
return;
|
245
|
+
}
|
246
|
+
|
247
|
+
this->process_motions_(old_motion_int);
|
248
|
+
}
|
249
|
+
|
250
|
+
void MSA3xxComponent::update() {
|
251
|
+
ESP_LOGV(TAG, "Updating MSA3xx...");
|
252
|
+
|
253
|
+
if (!this->is_ready()) {
|
254
|
+
ESP_LOGV(TAG, "Component MSA3xx not ready for update");
|
255
|
+
return;
|
256
|
+
}
|
257
|
+
ESP_LOGV(TAG, "Acceleration: {x = %+1.3f m/s², y = %+1.3f m/s², z = %+1.3f m/s²}; ", this->data_.x, this->data_.y,
|
258
|
+
this->data_.z);
|
259
|
+
|
260
|
+
ESP_LOGV(TAG, "Orientation: {XY = %s, Z = %s}", orientation_xy_to_string(this->status_.orientation.orient_xy),
|
261
|
+
orientation_z_to_string(this->status_.orientation.orient_z));
|
262
|
+
|
263
|
+
#ifdef USE_SENSOR
|
264
|
+
if (this->acceleration_x_sensor_ != nullptr)
|
265
|
+
this->acceleration_x_sensor_->publish_state(this->data_.x);
|
266
|
+
if (this->acceleration_y_sensor_ != nullptr)
|
267
|
+
this->acceleration_y_sensor_->publish_state(this->data_.y);
|
268
|
+
if (this->acceleration_z_sensor_ != nullptr)
|
269
|
+
this->acceleration_z_sensor_->publish_state(this->data_.z);
|
270
|
+
#endif
|
271
|
+
|
272
|
+
#ifdef USE_TEXT_SENSOR
|
273
|
+
if (this->orientation_xy_text_sensor_ != nullptr &&
|
274
|
+
(this->status_.orientation.orient_xy != this->status_.orientation_old.orient_xy ||
|
275
|
+
this->status_.never_published)) {
|
276
|
+
this->orientation_xy_text_sensor_->publish_state(orientation_xy_to_string(this->status_.orientation.orient_xy));
|
277
|
+
}
|
278
|
+
if (this->orientation_z_text_sensor_ != nullptr &&
|
279
|
+
(this->status_.orientation.orient_z != this->status_.orientation_old.orient_z || this->status_.never_published)) {
|
280
|
+
this->orientation_z_text_sensor_->publish_state(orientation_z_to_string(this->status_.orientation.orient_z));
|
281
|
+
}
|
282
|
+
this->status_.orientation_old = this->status_.orientation;
|
283
|
+
#endif
|
284
|
+
|
285
|
+
this->status_.never_published = false;
|
286
|
+
this->status_clear_warning();
|
287
|
+
}
|
288
|
+
float MSA3xxComponent::get_setup_priority() const { return setup_priority::DATA; }
|
289
|
+
|
290
|
+
void MSA3xxComponent::set_offset(float offset_x, float offset_y, float offset_z) {
|
291
|
+
this->offset_x_ = offset_x;
|
292
|
+
this->offset_y_ = offset_y;
|
293
|
+
this->offset_z_ = offset_z;
|
294
|
+
}
|
295
|
+
|
296
|
+
void MSA3xxComponent::set_transform(bool mirror_x, bool mirror_y, bool mirror_z, bool swap_xy) {
|
297
|
+
this->swap_.x_polarity = mirror_x;
|
298
|
+
this->swap_.y_polarity = mirror_y;
|
299
|
+
this->swap_.z_polarity = mirror_z;
|
300
|
+
this->swap_.x_y_swap = swap_xy;
|
301
|
+
}
|
302
|
+
|
303
|
+
void MSA3xxComponent::setup_odr_(DataRate rate) {
|
304
|
+
RegOutputDataRate reg_odr;
|
305
|
+
auto reg = this->read_byte(static_cast<uint8_t>(RegisterMap::ODR));
|
306
|
+
if (reg.has_value()) {
|
307
|
+
reg_odr.raw = reg.value();
|
308
|
+
} else {
|
309
|
+
reg_odr.raw = 0x0F; // defaut from datasheet
|
310
|
+
}
|
311
|
+
|
312
|
+
reg_odr.x_axis_disable = false;
|
313
|
+
reg_odr.y_axis_disable = false;
|
314
|
+
reg_odr.z_axis_disable = false;
|
315
|
+
reg_odr.odr = rate;
|
316
|
+
|
317
|
+
this->write_byte(static_cast<uint8_t>(RegisterMap::ODR), reg_odr.raw);
|
318
|
+
}
|
319
|
+
|
320
|
+
void MSA3xxComponent::setup_power_mode_bandwidth_(PowerMode power_mode, Bandwidth bandwidth) {
|
321
|
+
// 0x11 POWER_MODE_BANDWIDTH
|
322
|
+
auto reg = this->read_byte(static_cast<uint8_t>(RegisterMap::POWER_MODE_BANDWIDTH));
|
323
|
+
|
324
|
+
RegPowerModeBandwidth power_mode_bandwidth;
|
325
|
+
if (reg.has_value()) {
|
326
|
+
power_mode_bandwidth.raw = reg.value();
|
327
|
+
} else {
|
328
|
+
power_mode_bandwidth.raw = 0xde; // defaut from datasheet
|
329
|
+
}
|
330
|
+
|
331
|
+
power_mode_bandwidth.power_mode = power_mode;
|
332
|
+
power_mode_bandwidth.low_power_bandwidth = bandwidth;
|
333
|
+
|
334
|
+
this->write_byte(static_cast<uint8_t>(RegisterMap::POWER_MODE_BANDWIDTH), power_mode_bandwidth.raw);
|
335
|
+
}
|
336
|
+
|
337
|
+
void MSA3xxComponent::setup_range_resolution_(Range range, Resolution resolution) {
|
338
|
+
RegRangeResolution reg;
|
339
|
+
reg.raw = this->read_byte(static_cast<uint8_t>(RegisterMap::RANGE_RESOLUTION)).value_or(0x00);
|
340
|
+
reg.range = range;
|
341
|
+
if (this->model_ == Model::MSA301) {
|
342
|
+
reg.resolution = resolution;
|
343
|
+
}
|
344
|
+
this->write_byte(static_cast<uint8_t>(RegisterMap::RANGE_RESOLUTION), reg.raw);
|
345
|
+
}
|
346
|
+
|
347
|
+
void MSA3xxComponent::setup_offset_(float offset_x, float offset_y, float offset_z) {
|
348
|
+
uint8_t offset[3];
|
349
|
+
|
350
|
+
auto offset_g_to_lsb = [](float accel) -> int8_t {
|
351
|
+
float acccel_clamped = clamp(accel, G_OFFSET_MIN, G_OFFSET_MAX);
|
352
|
+
return static_cast<int8_t>(acccel_clamped * LSB_COEFF);
|
353
|
+
};
|
354
|
+
|
355
|
+
offset[0] = offset_g_to_lsb(offset_x);
|
356
|
+
offset[1] = offset_g_to_lsb(offset_y);
|
357
|
+
offset[2] = offset_g_to_lsb(offset_z);
|
358
|
+
|
359
|
+
ESP_LOGV(TAG, "Offset (%.3f, %.3f, %.3f)=>LSB(%d, %d, %d)", offset_x, offset_y, offset_z, offset[0], offset[1],
|
360
|
+
offset[2]);
|
361
|
+
|
362
|
+
this->write_bytes(static_cast<uint8_t>(RegisterMap::OFFSET_COMP_X), (uint8_t *) &offset, 3);
|
363
|
+
}
|
364
|
+
|
365
|
+
int64_t MSA3xxComponent::twos_complement_(uint64_t value, uint8_t bits) {
|
366
|
+
if (value > (1ULL << (bits - 1))) {
|
367
|
+
return (int64_t) (value - (1ULL << bits));
|
368
|
+
} else {
|
369
|
+
return (int64_t) value;
|
370
|
+
}
|
371
|
+
}
|
372
|
+
|
373
|
+
void binary_event_debounce(bool state, bool old_state, uint32_t now, uint32_t &last_ms, Trigger<> &trigger,
|
374
|
+
uint32_t cooldown_ms, void *bs, const char *desc) {
|
375
|
+
if (state && now - last_ms > cooldown_ms) {
|
376
|
+
ESP_LOGV(TAG, "%s detected", desc);
|
377
|
+
trigger.trigger();
|
378
|
+
last_ms = now;
|
379
|
+
#ifdef USE_BINARY_SENSOR
|
380
|
+
if (bs != nullptr) {
|
381
|
+
static_cast<binary_sensor::BinarySensor *>(bs)->publish_state(true);
|
382
|
+
}
|
383
|
+
#endif
|
384
|
+
} else if (!state && now - last_ms > cooldown_ms && bs != nullptr) {
|
385
|
+
#ifdef USE_BINARY_SENSOR
|
386
|
+
static_cast<binary_sensor::BinarySensor *>(bs)->publish_state(false);
|
387
|
+
#endif
|
388
|
+
}
|
389
|
+
}
|
390
|
+
|
391
|
+
#ifdef USE_BINARY_SENSOR
|
392
|
+
#define BS_OPTIONAL_PTR(x) ((void *) (x))
|
393
|
+
#else
|
394
|
+
#define BS_OPTIONAL_PTR(x) (nullptr)
|
395
|
+
#endif
|
396
|
+
|
397
|
+
void MSA3xxComponent::process_motions_(RegMotionInterrupt old) {
|
398
|
+
uint32_t now = millis();
|
399
|
+
|
400
|
+
binary_event_debounce(this->status_.motion_int.single_tap_interrupt, old.single_tap_interrupt, now,
|
401
|
+
this->status_.last_tap_ms, this->tap_trigger_, TAP_COOLDOWN_MS,
|
402
|
+
BS_OPTIONAL_PTR(this->tap_binary_sensor_), "Tap");
|
403
|
+
binary_event_debounce(this->status_.motion_int.double_tap_interrupt, old.double_tap_interrupt, now,
|
404
|
+
this->status_.last_double_tap_ms, this->double_tap_trigger_, DOUBLE_TAP_COOLDOWN_MS,
|
405
|
+
BS_OPTIONAL_PTR(this->double_tap_binary_sensor_), "Double Tap");
|
406
|
+
binary_event_debounce(this->status_.motion_int.active_interrupt, old.active_interrupt, now,
|
407
|
+
this->status_.last_action_ms, this->active_trigger_, ACTIVITY_COOLDOWN_MS,
|
408
|
+
BS_OPTIONAL_PTR(this->active_binary_sensor_), "Activity");
|
409
|
+
|
410
|
+
if (this->status_.motion_int.orientation_interrupt) {
|
411
|
+
ESP_LOGVV(TAG, "Orientation changed");
|
412
|
+
this->orientation_trigger_.trigger();
|
413
|
+
}
|
414
|
+
}
|
415
|
+
|
416
|
+
} // namespace msa3xx
|
417
|
+
} // namespace esphome
|