mirror of
https://github.com/esphome/esphome.git
synced 2026-10-27 00:12:11 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29bc2c1275 | ||
|
|
e7dd4f06ea | ||
|
|
eabc7df6fc | ||
|
|
650db15e2b |
No files matched your search
@@ -162,6 +162,11 @@ void SendspinHub::setup() {
|
||||
if (this->unpaired_access_.has_value()) {
|
||||
this->client_->set_unpaired_access_enabled(*this->unpaired_access_);
|
||||
}
|
||||
|
||||
#ifdef USE_SENDSPIN_ENABLED_SWITCH
|
||||
// loop() waits for this, so a restore mode without a state still has to answer.
|
||||
this->enabled_switch_->restore(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SendspinHub::loop() {
|
||||
@@ -211,6 +216,9 @@ void SendspinHub::dump_config() {
|
||||
this->firmware_version_, YESNO(this->task_stack_in_psram_),
|
||||
YESNO(this->client_ != nullptr && this->client_->is_unpaired_access_enabled()),
|
||||
this->pairing_code_method_());
|
||||
#ifdef USE_SENDSPIN_ENABLED_SWITCH
|
||||
LOG_SWITCH(" ", "Enabled switch", this->enabled_switch_);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENDSPIN_ARTWORK
|
||||
// Slot indices come from the order the image platform entries were declared, so the log is the
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#ifdef USE_MDNS_SUPPORTS_ENABLE_DISABLE
|
||||
#include "esphome/components/mdns/mdns_component.h"
|
||||
#endif
|
||||
#ifdef USE_SENDSPIN_ENABLED_SWITCH
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#endif
|
||||
|
||||
#include <sendspin/client.h>
|
||||
#include <sendspin/config.h>
|
||||
@@ -178,6 +181,9 @@ class SendspinHub final : public Component,
|
||||
/// fire from inside that call. The client stays stopped until this has been called once, by codegen or by the
|
||||
/// enabled switch. Must be called from the main loop thread.
|
||||
void set_enabled(bool enabled);
|
||||
#ifdef USE_SENDSPIN_ENABLED_SWITCH
|
||||
void set_enabled_switch(switch_::Switch *sw) { this->enabled_switch_ = sw; }
|
||||
#endif
|
||||
|
||||
/// Turns unpaired (Sentinel) access on or off. The client waits for this call, by codegen or by the unpaired
|
||||
/// access switch, before its first start. May be called before setup(). Main loop only.
|
||||
@@ -407,6 +413,9 @@ class SendspinHub final : public Component,
|
||||
LazyCallbackManager<void(const std::string &, StringRef)> pairing_failed_callbacks_{};
|
||||
|
||||
const char *static_pairing_code_{nullptr}; // Codegen string literal, or nullptr when not configured
|
||||
#ifdef USE_SENDSPIN_ENABLED_SWITCH
|
||||
switch_::Switch *enabled_switch_{nullptr};
|
||||
#endif
|
||||
bool pairing_code_display_supported_{false};
|
||||
bool task_stack_in_psram_{false};
|
||||
#ifdef USE_MDNS_SUPPORTS_ENABLE_DISABLE
|
||||
|
||||
@@ -25,25 +25,18 @@ _LOGGER = logging.getLogger(__name__)
|
||||
CODEOWNERS = ["@kahrendt"]
|
||||
DEPENDENCIES = ["sendspin"]
|
||||
|
||||
SendspinEnabledSwitch = sendspin_ns.class_(
|
||||
"SendspinEnabledSwitch", switch.Switch, cg.Component
|
||||
)
|
||||
SendspinUnpairedAccessSwitch = sendspin_ns.class_(
|
||||
"SendspinUnpairedAccessSwitch", switch.Switch, cg.Component
|
||||
)
|
||||
|
||||
|
||||
def _switch_schema(class_: cg.MockObjClass) -> cv.Schema:
|
||||
return (
|
||||
switch.switch_schema(
|
||||
class_,
|
||||
block_inverted=True,
|
||||
default_restore_mode="RESTORE_DEFAULT_ON",
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
)
|
||||
.extend({cv.GenerateID(CONF_SENDSPIN_ID): cv.use_id(SendspinHub)})
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
return switch.switch_schema(
|
||||
class_,
|
||||
block_inverted=True,
|
||||
default_restore_mode="RESTORE_DEFAULT_ON",
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
).extend({cv.GenerateID(CONF_SENDSPIN_ID): cv.use_id(SendspinHub)})
|
||||
|
||||
|
||||
def _request_switch(config: ConfigType) -> ConfigType:
|
||||
@@ -55,8 +48,10 @@ def _request_switch(config: ConfigType) -> ConfigType:
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.typed_schema(
|
||||
{
|
||||
CONF_ENABLED: _switch_schema(SendspinEnabledSwitch),
|
||||
CONF_UNPAIRED_ACCESS: _switch_schema(SendspinUnpairedAccessSwitch),
|
||||
CONF_ENABLED: _switch_schema(switch.ApplySwitch),
|
||||
CONF_UNPAIRED_ACCESS: _switch_schema(SendspinUnpairedAccessSwitch).extend(
|
||||
cv.COMPONENT_SCHEMA
|
||||
),
|
||||
},
|
||||
key=CONF_TYPE,
|
||||
),
|
||||
@@ -98,6 +93,15 @@ FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
if config[CONF_TYPE] == CONF_ENABLED:
|
||||
# The hub restores this switch in its setup() and logs it.
|
||||
hub = await cg.get_variable(config[CONF_SENDSPIN_ID])
|
||||
switches = switch.sub_switches(config, parent=hub)
|
||||
await switches.create(
|
||||
config, hub.set_enabled_switch, apply="set_enabled(state)", publish="after"
|
||||
)
|
||||
cg.add_define("USE_SENDSPIN_ENABLED_SWITCH", True)
|
||||
return
|
||||
var = await switch.new_switch(config)
|
||||
await cg.register_component(var, config)
|
||||
await cg.register_parented(var, config[CONF_SENDSPIN_ID])
|
||||
|
||||
@@ -9,21 +9,6 @@ namespace esphome::sendspin_ {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "sendspin.switch");
|
||||
|
||||
// --- SendspinEnabledSwitch ---
|
||||
|
||||
void SendspinEnabledSwitch::setup() {
|
||||
// The hub waits for this request, so a restore mode without a state still has to answer.
|
||||
this->restore(true);
|
||||
}
|
||||
|
||||
void SendspinEnabledSwitch::dump_config() { LOG_SWITCH("", "Sendspin Enabled Switch", this); }
|
||||
|
||||
// THREAD CONTEXT: Main loop
|
||||
void SendspinEnabledSwitch::write_state(bool state) {
|
||||
this->parent_->set_enabled(state);
|
||||
this->publish_state(state);
|
||||
}
|
||||
|
||||
// --- SendspinUnpairedAccessSwitch ---
|
||||
|
||||
void SendspinUnpairedAccessSwitch::setup() {
|
||||
|
||||
@@ -9,16 +9,6 @@
|
||||
|
||||
namespace esphome::sendspin_ {
|
||||
|
||||
/// @brief Switch that starts and stops the Sendspin client through the hub (see SendspinHub::set_enabled()).
|
||||
class SendspinEnabledSwitch final : public switch_::Switch, public SendspinChild {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
};
|
||||
|
||||
/// @brief Switch that turns unpaired (Sentinel) access on and off through the hub (see
|
||||
/// SendspinHub::set_unpaired_access_enabled()). The library does not persist the setting, so the switch's restore
|
||||
/// mode does.
|
||||
|
||||
@@ -525,6 +525,7 @@
|
||||
#define USE_SENDSPIN
|
||||
#define USE_SENDSPIN_ARTWORK
|
||||
#define USE_SENDSPIN_CONTROLLER
|
||||
#define USE_SENDSPIN_ENABLED_SWITCH
|
||||
#define USE_SENDSPIN_METADATA
|
||||
#define USE_SENDSPIN_PLAYER
|
||||
#define USE_SENDSPIN_SWITCH
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
wifi:
|
||||
ap:
|
||||
|
||||
sendspin:
|
||||
|
||||
switch:
|
||||
- platform: sendspin
|
||||
type: enabled
|
||||
id: sendspin_enabled
|
||||
name: Sendspin Enabled
|
||||
@@ -0,0 +1,19 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
wifi:
|
||||
ap:
|
||||
|
||||
sendspin:
|
||||
|
||||
switch:
|
||||
- platform: sendspin
|
||||
type: enabled
|
||||
id: sendspin_enabled
|
||||
name: Sendspin Enabled
|
||||
restore_mode: DISABLED
|
||||
@@ -0,0 +1,59 @@
|
||||
"""Code generation tests for the enabled switch.
|
||||
|
||||
The client stays stopped until the hub has an enabled state: codegen sets it when
|
||||
no enabled switch is configured, and the switch answers from its restore otherwise.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
GenerateMain = Callable[[str | Path], str]
|
||||
ConfigPath = Callable[[str], Path]
|
||||
|
||||
|
||||
def test_without_switch_codegen_enables_the_client(
|
||||
generate_main: GenerateMain, component_config_path: ConfigPath
|
||||
) -> None:
|
||||
"""With no enabled switch, nothing else would start the client."""
|
||||
main_cpp = generate_main(component_config_path("device_info_default.yaml"))
|
||||
|
||||
assert "->set_enabled(true);" in main_cpp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("config", "restore_mode"),
|
||||
[
|
||||
("switch_enabled.yaml", "SWITCH_RESTORE_DEFAULT_ON"),
|
||||
("switch_enabled_disabled_restore.yaml", "SWITCH_RESTORE_DISABLED"),
|
||||
],
|
||||
)
|
||||
def test_switch_answers_the_hub(
|
||||
generate_main: GenerateMain,
|
||||
component_config_path: ConfigPath,
|
||||
config: str,
|
||||
restore_mode: str,
|
||||
) -> None:
|
||||
"""The hub waits for the switch's restore instead of being enabled by codegen."""
|
||||
main_cpp = generate_main(component_config_path(config))
|
||||
|
||||
assert "->set_enabled(true);" not in main_cpp
|
||||
assert f"sendspin_enabled->set_restore_mode(switch_::{restore_mode});" in main_cpp
|
||||
assert "App.register_switch(sendspin_enabled," in main_cpp
|
||||
|
||||
|
||||
def test_switch_sets_the_hub_then_publishes(
|
||||
generate_main: GenerateMain, component_config_path: ConfigPath
|
||||
) -> None:
|
||||
"""Turning the switch sets the hub first, then publishes."""
|
||||
main_cpp = generate_main(component_config_path("switch_enabled.yaml"))
|
||||
|
||||
# The generated body is the hub call followed directly by the publish
|
||||
assert re.search(
|
||||
r"->set_enabled\(state\);\s*entity->publish_state\(state\);", main_cpp
|
||||
)
|
||||
assert "->set_enabled_switch(sendspin_enabled);" in main_cpp
|
||||
Reference in new issue
Block a user