mirror of
https://github.com/esphome/esphome.git
synced 2024-11-24 16:08:10 +01:00
Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
fdf50b0c03
128 changed files with 3249 additions and 419 deletions
2
.github/actions/restore-python/action.yml
vendored
2
.github/actions/restore-python/action.yml
vendored
|
@ -17,7 +17,7 @@ runs:
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Python ${{ inputs.python-version }}
|
- name: Set up Python ${{ inputs.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ inputs.python-version }}
|
python-version: ${{ inputs.python-version }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
|
|
2
.github/workflows/ci-api-proto.yml
vendored
2
.github/workflows/ci-api-proto.yml
vendored
|
@ -23,7 +23,7 @@ jobs:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
|
|
4
.github/workflows/ci-docker.yml
vendored
4
.github/workflows/ci-docker.yml
vendored
|
@ -42,11 +42,11 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.1
|
- uses: actions/checkout@v4.1.1
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.2.0
|
uses: docker/setup-buildx-action@v3.3.0
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3.0.0
|
uses: docker/setup-qemu-action@v3.0.0
|
||||||
|
|
||||||
|
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -41,7 +41,7 @@ jobs:
|
||||||
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
|
|
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
|
@ -44,7 +44,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.1
|
- uses: actions/checkout@v4.1.1
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Set up python environment
|
- name: Set up python environment
|
||||||
|
@ -79,12 +79,12 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.1
|
- uses: actions/checkout@v4.1.1
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.2.0
|
uses: docker/setup-buildx-action@v3.3.0
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
if: matrix.platform != 'linux/amd64'
|
if: matrix.platform != 'linux/amd64'
|
||||||
uses: docker/setup-qemu-action@v3.0.0
|
uses: docker/setup-qemu-action@v3.0.0
|
||||||
|
@ -162,7 +162,7 @@ jobs:
|
||||||
name: digests-${{ matrix.image.target }}-${{ matrix.registry }}
|
name: digests-${{ matrix.image.target }}-${{ matrix.registry }}
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.2.0
|
uses: docker/setup-buildx-action@v3.3.0
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
if: matrix.registry == 'dockerhub'
|
if: matrix.registry == 'dockerhub'
|
||||||
|
|
2
.github/workflows/sync-device-classes.yml
vendored
2
.github/workflows/sync-device-classes.yml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
||||||
path: lib/home-assistant
|
path: lib/home-assistant
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5.0.0
|
uses: actions/setup-python@v5.1.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.11
|
python-version: 3.11
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ esphome/components/daikin_arc/* @MagicBear
|
||||||
esphome/components/daikin_brc/* @hagak
|
esphome/components/daikin_brc/* @hagak
|
||||||
esphome/components/daly_bms/* @s1lvi0
|
esphome/components/daly_bms/* @s1lvi0
|
||||||
esphome/components/dashboard_import/* @esphome/core
|
esphome/components/dashboard_import/* @esphome/core
|
||||||
esphome/components/datetime/* @rfdarter
|
esphome/components/datetime/* @jesserockz @rfdarter
|
||||||
esphome/components/debug/* @OttoWinter
|
esphome/components/debug/* @OttoWinter
|
||||||
esphome/components/delonghi/* @grob6000
|
esphome/components/delonghi/* @grob6000
|
||||||
esphome/components/dfplayer/* @glmnet
|
esphome/components/dfplayer/* @glmnet
|
||||||
|
@ -116,6 +116,7 @@ esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz
|
||||||
esphome/components/esp32_camera_web_server/* @ayufan
|
esphome/components/esp32_camera_web_server/* @ayufan
|
||||||
esphome/components/esp32_can/* @Sympatron
|
esphome/components/esp32_can/* @Sympatron
|
||||||
esphome/components/esp32_improv/* @jesserockz
|
esphome/components/esp32_improv/* @jesserockz
|
||||||
|
esphome/components/esp32_rmt/* @jesserockz
|
||||||
esphome/components/esp32_rmt_led_strip/* @jesserockz
|
esphome/components/esp32_rmt_led_strip/* @jesserockz
|
||||||
esphome/components/esp8266/* @esphome/core
|
esphome/components/esp8266/* @esphome/core
|
||||||
esphome/components/ethernet_info/* @gtjadsonsantos
|
esphome/components/ethernet_info/* @gtjadsonsantos
|
||||||
|
@ -405,6 +406,7 @@ esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard
|
||||||
esphome/components/wl_134/* @hobbypunk90
|
esphome/components/wl_134/* @hobbypunk90
|
||||||
esphome/components/x9c/* @EtienneMD
|
esphome/components/x9c/* @EtienneMD
|
||||||
esphome/components/xgzp68xx/* @gcormier
|
esphome/components/xgzp68xx/* @gcormier
|
||||||
|
esphome/components/xiaomi_hhccjcy10/* @fariouche
|
||||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
||||||
esphome/components/xiaomi_mhoc303/* @drug123
|
esphome/components/xiaomi_mhoc303/* @drug123
|
||||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||||
|
|
|
@ -45,6 +45,7 @@ service APIConnection {
|
||||||
rpc lock_command (LockCommandRequest) returns (void) {}
|
rpc lock_command (LockCommandRequest) returns (void) {}
|
||||||
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
|
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
|
||||||
rpc date_command (DateCommandRequest) returns (void) {}
|
rpc date_command (DateCommandRequest) returns (void) {}
|
||||||
|
rpc time_command (TimeCommandRequest) returns (void) {}
|
||||||
|
|
||||||
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
||||||
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
|
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
|
||||||
|
@ -217,7 +218,8 @@ message DeviceInfoResponse {
|
||||||
|
|
||||||
string friendly_name = 13;
|
string friendly_name = 13;
|
||||||
|
|
||||||
uint32 voice_assistant_version = 14;
|
uint32 legacy_voice_assistant_version = 14;
|
||||||
|
uint32 voice_assistant_feature_flags = 17;
|
||||||
|
|
||||||
string suggested_area = 16;
|
string suggested_area = 16;
|
||||||
}
|
}
|
||||||
|
@ -1422,12 +1424,18 @@ message BluetoothDeviceClearCacheResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== PUSH TO TALK ====================
|
// ==================== PUSH TO TALK ====================
|
||||||
|
enum VoiceAssistantSubscribeFlag {
|
||||||
|
VOICE_ASSISTANT_SUBSCRIBE_NONE = 0;
|
||||||
|
VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message SubscribeVoiceAssistantRequest {
|
message SubscribeVoiceAssistantRequest {
|
||||||
option (id) = 89;
|
option (id) = 89;
|
||||||
option (source) = SOURCE_CLIENT;
|
option (source) = SOURCE_CLIENT;
|
||||||
option (ifdef) = "USE_VOICE_ASSISTANT";
|
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||||
|
|
||||||
bool subscribe = 1;
|
bool subscribe = 1;
|
||||||
|
uint32 flags = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum VoiceAssistantRequestFlag {
|
enum VoiceAssistantRequestFlag {
|
||||||
|
@ -1495,6 +1503,16 @@ message VoiceAssistantEventResponse {
|
||||||
repeated VoiceAssistantEventData data = 2;
|
repeated VoiceAssistantEventData data = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message VoiceAssistantAudio {
|
||||||
|
option (id) = 106;
|
||||||
|
option (source) = SOURCE_BOTH;
|
||||||
|
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||||
|
|
||||||
|
bytes data = 1;
|
||||||
|
bool end = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ==================== ALARM CONTROL PANEL ====================
|
// ==================== ALARM CONTROL PANEL ====================
|
||||||
enum AlarmControlPanelState {
|
enum AlarmControlPanelState {
|
||||||
ALARM_STATE_DISARMED = 0;
|
ALARM_STATE_DISARMED = 0;
|
||||||
|
@ -1641,3 +1659,44 @@ message DateCommandRequest {
|
||||||
uint32 month = 3;
|
uint32 month = 3;
|
||||||
uint32 day = 4;
|
uint32 day = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== DATETIME TIME ====================
|
||||||
|
message ListEntitiesTimeResponse {
|
||||||
|
option (id) = 103;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_DATETIME_TIME";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
|
||||||
|
string icon = 5;
|
||||||
|
bool disabled_by_default = 6;
|
||||||
|
EntityCategory entity_category = 7;
|
||||||
|
}
|
||||||
|
message TimeStateResponse {
|
||||||
|
option (id) = 104;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_DATETIME_TIME";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
// If the time does not have a valid state yet.
|
||||||
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
|
bool missing_state = 2;
|
||||||
|
uint32 hour = 3;
|
||||||
|
uint32 minute = 4;
|
||||||
|
uint32 second = 5;
|
||||||
|
}
|
||||||
|
message TimeCommandRequest {
|
||||||
|
option (id) = 105;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_DATETIME_TIME";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
uint32 hour = 2;
|
||||||
|
uint32 minute = 3;
|
||||||
|
uint32 second = 4;
|
||||||
|
}
|
||||||
|
|
|
@ -735,6 +735,43 @@ void APIConnection::date_command(const DateCommandRequest &msg) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool APIConnection::send_time_state(datetime::TimeEntity *time) {
|
||||||
|
if (!this->state_subscription_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TimeStateResponse resp{};
|
||||||
|
resp.key = time->get_object_id_hash();
|
||||||
|
resp.missing_state = !time->has_state();
|
||||||
|
resp.hour = time->hour;
|
||||||
|
resp.minute = time->minute;
|
||||||
|
resp.second = time->second;
|
||||||
|
return this->send_time_state_response(resp);
|
||||||
|
}
|
||||||
|
bool APIConnection::send_time_info(datetime::TimeEntity *time) {
|
||||||
|
ListEntitiesTimeResponse msg;
|
||||||
|
msg.key = time->get_object_id_hash();
|
||||||
|
msg.object_id = time->get_object_id();
|
||||||
|
if (time->has_own_name())
|
||||||
|
msg.name = time->get_name();
|
||||||
|
msg.unique_id = get_default_unique_id("time", time);
|
||||||
|
msg.icon = time->get_icon();
|
||||||
|
msg.disabled_by_default = time->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(time->get_entity_category());
|
||||||
|
|
||||||
|
return this->send_list_entities_time_response(msg);
|
||||||
|
}
|
||||||
|
void APIConnection::time_command(const TimeCommandRequest &msg) {
|
||||||
|
datetime::TimeEntity *time = App.get_time_by_key(msg.key);
|
||||||
|
if (time == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto call = time->make_call();
|
||||||
|
call.set_time(msg.hour, msg.minute, msg.second);
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool APIConnection::send_text_state(text::Text *text, std::string state) {
|
bool APIConnection::send_text_state(text::Text *text, std::string state) {
|
||||||
if (!this->state_subscription_)
|
if (!this->state_subscription_)
|
||||||
|
@ -1040,10 +1077,15 @@ void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &ms
|
||||||
voice_assistant::global_voice_assistant->failed_to_start();
|
voice_assistant::global_voice_assistant->failed_to_start();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct sockaddr_storage storage;
|
if (msg.port == 0) {
|
||||||
socklen_t len = sizeof(storage);
|
// Use API Audio
|
||||||
this->helper_->getpeername((struct sockaddr *) &storage, &len);
|
voice_assistant::global_voice_assistant->start_streaming();
|
||||||
voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
|
} else {
|
||||||
|
struct sockaddr_storage storage;
|
||||||
|
socklen_t len = sizeof(storage);
|
||||||
|
this->helper_->getpeername((struct sockaddr *) &storage, &len);
|
||||||
|
voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
|
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
|
||||||
|
@ -1055,6 +1097,15 @@ void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventR
|
||||||
voice_assistant::global_voice_assistant->on_event(msg);
|
voice_assistant::global_voice_assistant->on_event(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void APIConnection::on_voice_assistant_audio(const VoiceAssistantAudio &msg) {
|
||||||
|
if (voice_assistant::global_voice_assistant != nullptr) {
|
||||||
|
if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice_assistant::global_voice_assistant->on_audio(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1142,7 +1193,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
||||||
|
|
||||||
HelloResponse resp;
|
HelloResponse resp;
|
||||||
resp.api_version_major = 1;
|
resp.api_version_major = 1;
|
||||||
resp.api_version_minor = 9;
|
resp.api_version_minor = 10;
|
||||||
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||||
resp.name = App.get_name();
|
resp.name = App.get_name();
|
||||||
|
|
||||||
|
@ -1203,7 +1254,8 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
||||||
resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
|
resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
resp.voice_assistant_version = voice_assistant::global_voice_assistant->get_version();
|
resp.legacy_voice_assistant_version = voice_assistant::global_voice_assistant->get_legacy_version();
|
||||||
|
resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
|
||||||
#endif
|
#endif
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,11 @@ class APIConnection : public APIServerConnection {
|
||||||
bool send_date_info(datetime::DateEntity *date);
|
bool send_date_info(datetime::DateEntity *date);
|
||||||
void date_command(const DateCommandRequest &msg) override;
|
void date_command(const DateCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool send_time_state(datetime::TimeEntity *time);
|
||||||
|
bool send_time_info(datetime::TimeEntity *time);
|
||||||
|
void time_command(const TimeCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool send_text_state(text::Text *text, std::string state);
|
bool send_text_state(text::Text *text, std::string state);
|
||||||
bool send_text_info(text::Text *text);
|
bool send_text_info(text::Text *text);
|
||||||
|
@ -134,6 +139,7 @@ class APIConnection : public APIServerConnection {
|
||||||
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override;
|
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override;
|
||||||
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
||||||
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
||||||
|
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
|
|
@ -410,6 +410,19 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
template<>
|
||||||
|
const char *proto_enum_to_string<enums::VoiceAssistantSubscribeFlag>(enums::VoiceAssistantSubscribeFlag value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::VOICE_ASSISTANT_SUBSCRIBE_NONE:
|
||||||
|
return "VOICE_ASSISTANT_SUBSCRIBE_NONE";
|
||||||
|
case enums::VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO:
|
||||||
|
return "VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
template<> const char *proto_enum_to_string<enums::VoiceAssistantRequestFlag>(enums::VoiceAssistantRequestFlag value) {
|
template<> const char *proto_enum_to_string<enums::VoiceAssistantRequestFlag>(enums::VoiceAssistantRequestFlag value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case enums::VOICE_ASSISTANT_REQUEST_NONE:
|
case enums::VOICE_ASSISTANT_REQUEST_NONE:
|
||||||
|
@ -716,7 +729,11 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 14: {
|
case 14: {
|
||||||
this->voice_assistant_version = value.as_uint32();
|
this->legacy_voice_assistant_version = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 17: {
|
||||||
|
this->voice_assistant_feature_flags = value.as_uint32();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -784,7 +801,8 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_uint32(15, this->bluetooth_proxy_feature_flags);
|
buffer.encode_uint32(15, this->bluetooth_proxy_feature_flags);
|
||||||
buffer.encode_string(12, this->manufacturer);
|
buffer.encode_string(12, this->manufacturer);
|
||||||
buffer.encode_string(13, this->friendly_name);
|
buffer.encode_string(13, this->friendly_name);
|
||||||
buffer.encode_uint32(14, this->voice_assistant_version);
|
buffer.encode_uint32(14, this->legacy_voice_assistant_version);
|
||||||
|
buffer.encode_uint32(17, this->voice_assistant_feature_flags);
|
||||||
buffer.encode_string(16, this->suggested_area);
|
buffer.encode_string(16, this->suggested_area);
|
||||||
}
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
@ -850,8 +868,13 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||||
out.append("'").append(this->friendly_name).append("'");
|
out.append("'").append(this->friendly_name).append("'");
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" voice_assistant_version: ");
|
out.append(" legacy_voice_assistant_version: ");
|
||||||
sprintf(buffer, "%" PRIu32, this->voice_assistant_version);
|
sprintf(buffer, "%" PRIu32, this->legacy_voice_assistant_version);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" voice_assistant_feature_flags: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->voice_assistant_feature_flags);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -6514,11 +6537,18 @@ bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarIn
|
||||||
this->subscribe = value.as_bool();
|
this->subscribe = value.as_bool();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 2: {
|
||||||
|
this->flags = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->subscribe); }
|
void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_bool(1, this->subscribe);
|
||||||
|
buffer.encode_uint32(2, this->flags);
|
||||||
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
|
void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
|
@ -6526,6 +6556,11 @@ void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
|
||||||
out.append(" subscribe: ");
|
out.append(" subscribe: ");
|
||||||
out.append(YESNO(this->subscribe));
|
out.append(YESNO(this->subscribe));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" flags: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->flags);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -6752,6 +6787,44 @@ void VoiceAssistantEventResponse::dump_to(std::string &out) const {
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
bool VoiceAssistantAudio::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->end = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool VoiceAssistantAudio::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->data = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void VoiceAssistantAudio::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_string(1, this->data);
|
||||||
|
buffer.encode_bool(2, this->end);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void VoiceAssistantAudio::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("VoiceAssistantAudio {\n");
|
||||||
|
out.append(" data: ");
|
||||||
|
out.append("'").append(this->data).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" end: ");
|
||||||
|
out.append(YESNO(this->end));
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 6: {
|
case 6: {
|
||||||
|
@ -7403,6 +7476,225 @@ void DateCommandRequest::dump_to(std::string &out) const {
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
bool ListEntitiesTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 6: {
|
||||||
|
this->disabled_by_default = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 7: {
|
||||||
|
this->entity_category = value.as_enum<enums::EntityCategory>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ListEntitiesTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->object_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->name = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->unique_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->icon = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ListEntitiesTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_string(1, this->object_id);
|
||||||
|
buffer.encode_fixed32(2, this->key);
|
||||||
|
buffer.encode_string(3, this->name);
|
||||||
|
buffer.encode_string(4, this->unique_id);
|
||||||
|
buffer.encode_string(5, this->icon);
|
||||||
|
buffer.encode_bool(6, this->disabled_by_default);
|
||||||
|
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void ListEntitiesTimeResponse::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("ListEntitiesTimeResponse {\n");
|
||||||
|
out.append(" object_id: ");
|
||||||
|
out.append("'").append(this->object_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" name: ");
|
||||||
|
out.append("'").append(this->name).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" unique_id: ");
|
||||||
|
out.append("'").append(this->unique_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" icon: ");
|
||||||
|
out.append("'").append(this->icon).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" disabled_by_default: ");
|
||||||
|
out.append(YESNO(this->disabled_by_default));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" entity_category: ");
|
||||||
|
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool TimeStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->missing_state = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->hour = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->minute = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->second = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool TimeStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TimeStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_fixed32(1, this->key);
|
||||||
|
buffer.encode_bool(2, this->missing_state);
|
||||||
|
buffer.encode_uint32(3, this->hour);
|
||||||
|
buffer.encode_uint32(4, this->minute);
|
||||||
|
buffer.encode_uint32(5, this->second);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void TimeStateResponse::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("TimeStateResponse {\n");
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" missing_state: ");
|
||||||
|
out.append(YESNO(this->missing_state));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" hour: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->hour);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" minute: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->minute);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" second: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->second);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool TimeCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->hour = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->minute = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->second = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TimeCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_fixed32(1, this->key);
|
||||||
|
buffer.encode_uint32(2, this->hour);
|
||||||
|
buffer.encode_uint32(3, this->minute);
|
||||||
|
buffer.encode_uint32(4, this->second);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void TimeCommandRequest::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("TimeCommandRequest {\n");
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" hour: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->hour);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" minute: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->minute);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" second: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->second);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -165,6 +165,10 @@ enum BluetoothDeviceRequestType : uint32_t {
|
||||||
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
|
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
|
||||||
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
|
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
|
||||||
};
|
};
|
||||||
|
enum VoiceAssistantSubscribeFlag : uint32_t {
|
||||||
|
VOICE_ASSISTANT_SUBSCRIBE_NONE = 0,
|
||||||
|
VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1,
|
||||||
|
};
|
||||||
enum VoiceAssistantRequestFlag : uint32_t {
|
enum VoiceAssistantRequestFlag : uint32_t {
|
||||||
VOICE_ASSISTANT_REQUEST_NONE = 0,
|
VOICE_ASSISTANT_REQUEST_NONE = 0,
|
||||||
VOICE_ASSISTANT_REQUEST_USE_VAD = 1,
|
VOICE_ASSISTANT_REQUEST_USE_VAD = 1,
|
||||||
|
@ -327,7 +331,8 @@ class DeviceInfoResponse : public ProtoMessage {
|
||||||
uint32_t bluetooth_proxy_feature_flags{0};
|
uint32_t bluetooth_proxy_feature_flags{0};
|
||||||
std::string manufacturer{};
|
std::string manufacturer{};
|
||||||
std::string friendly_name{};
|
std::string friendly_name{};
|
||||||
uint32_t voice_assistant_version{0};
|
uint32_t legacy_voice_assistant_version{0};
|
||||||
|
uint32_t voice_assistant_feature_flags{0};
|
||||||
std::string suggested_area{};
|
std::string suggested_area{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
@ -1674,6 +1679,7 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage {
|
||||||
class SubscribeVoiceAssistantRequest : public ProtoMessage {
|
class SubscribeVoiceAssistantRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
bool subscribe{false};
|
bool subscribe{false};
|
||||||
|
uint32_t flags{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
@ -1749,6 +1755,19 @@ class VoiceAssistantEventResponse : public ProtoMessage {
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
|
class VoiceAssistantAudio : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
std::string data{};
|
||||||
|
bool end{false};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{};
|
std::string object_id{};
|
||||||
|
@ -1900,6 +1919,56 @@ class DateCommandRequest : public ProtoMessage {
|
||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
|
class ListEntitiesTimeResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
std::string object_id{};
|
||||||
|
uint32_t key{0};
|
||||||
|
std::string name{};
|
||||||
|
std::string unique_id{};
|
||||||
|
std::string icon{};
|
||||||
|
bool disabled_by_default{false};
|
||||||
|
enums::EntityCategory entity_category{};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class TimeStateResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
uint32_t key{0};
|
||||||
|
bool missing_state{false};
|
||||||
|
uint32_t hour{0};
|
||||||
|
uint32_t minute{0};
|
||||||
|
uint32_t second{0};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class TimeCommandRequest : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
uint32_t key{0};
|
||||||
|
uint32_t hour{0};
|
||||||
|
uint32_t minute{0};
|
||||||
|
uint32_t second{0};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -476,6 +476,14 @@ bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantR
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAudio &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_voice_assistant_audio: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<VoiceAssistantAudio>(msg, 106);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
|
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
|
||||||
const ListEntitiesAlarmControlPanelResponse &msg) {
|
const ListEntitiesAlarmControlPanelResponse &msg) {
|
||||||
|
@ -531,6 +539,24 @@ bool APIServerConnectionBase::send_date_state_response(const DateStateResponse &
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool APIServerConnectionBase::send_list_entities_time_response(const ListEntitiesTimeResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_list_entities_time_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<ListEntitiesTimeResponse>(msg, 103);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_time_state_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<TimeStateResponse>(msg, 104);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
#endif
|
||||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||||
switch (msg_type) {
|
switch (msg_type) {
|
||||||
case 1: {
|
case 1: {
|
||||||
|
@ -971,6 +997,28 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||||
ESP_LOGVV(TAG, "on_date_command_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_date_command_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
this->on_date_command_request(msg);
|
this->on_date_command_request(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 105: {
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
TimeCommandRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_time_command_request: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_time_command_request(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 106: {
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
VoiceAssistantAudio msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_voice_assistant_audio: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_voice_assistant_audio(msg);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1260,6 +1308,19 @@ void APIServerConnection::on_date_command_request(const DateCommandRequest &msg)
|
||||||
this->date_command(msg);
|
this->date_command(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->time_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
|
|
|
@ -240,6 +240,10 @@ class APIServerConnectionBase : public ProtoService {
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
|
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
|
||||||
|
virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
|
||||||
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
|
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
|
||||||
#endif
|
#endif
|
||||||
|
@ -266,6 +270,15 @@ class APIServerConnectionBase : public ProtoService {
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
virtual void on_date_command_request(const DateCommandRequest &value){};
|
virtual void on_date_command_request(const DateCommandRequest &value){};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool send_list_entities_time_response(const ListEntitiesTimeResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool send_time_state_response(const TimeStateResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
virtual void on_time_command_request(const TimeCommandRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||||
|
@ -324,6 +337,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
virtual void date_command(const DateCommandRequest &msg) = 0;
|
virtual void date_command(const DateCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
virtual void time_command(const TimeCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
@ -413,6 +429,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
void on_date_command_request(const DateCommandRequest &msg) override;
|
void on_date_command_request(const DateCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void on_time_command_request(const TimeCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -264,6 +264,15 @@ void APIServer::on_date_update(datetime::DateEntity *obj) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void APIServer::on_time_update(datetime::TimeEntity *obj) {
|
||||||
|
if (obj->is_internal())
|
||||||
|
return;
|
||||||
|
for (auto &c : this->clients_)
|
||||||
|
c->send_time_state(obj);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
void APIServer::on_text_update(text::Text *obj, const std::string &state) {
|
void APIServer::on_text_update(text::Text *obj, const std::string &state) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
|
|
|
@ -69,6 +69,9 @@ class APIServer : public Component, public Controller {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
void on_date_update(datetime::DateEntity *obj) override;
|
void on_date_update(datetime::DateEntity *obj) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void on_time_update(datetime::TimeEntity *obj) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
void on_text_update(text::Text *obj, const std::string &state) override;
|
void on_text_update(text::Text *obj, const std::string &state) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -64,6 +64,10 @@ bool ListEntitiesIterator::on_number(number::Number *number) { return this->clie
|
||||||
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_info(date); }
|
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_info(date); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_info(time); }
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); }
|
bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); }
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -49,6 +49,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool on_date(datetime::DateEntity *date) override;
|
bool on_date(datetime::DateEntity *date) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool on_time(datetime::TimeEntity *time) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool on_text(text::Text *text) override;
|
bool on_text(text::Text *text) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -45,6 +45,9 @@ bool InitialStateIterator::on_number(number::Number *number) {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); }
|
bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); }
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool InitialStateIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_state(time); }
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); }
|
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); }
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -46,6 +46,9 @@ class InitialStateIterator : public ComponentIterator {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool on_date(datetime::DateEntity *date) override;
|
bool on_date(datetime::DateEntity *date) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool on_time(datetime::TimeEntity *time) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool on_text(text::Text *text) override;
|
bool on_text(text::Text *text) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -73,8 +73,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||||
break;
|
break;
|
||||||
case MATCH_BY_IRK:
|
case MATCH_BY_IRK:
|
||||||
if (resolve_irk_(device.address_uint64(), this->irk_)) {
|
if (resolve_irk_(device.address_uint64(), this->irk_)) {
|
||||||
this->publish_state(true);
|
this->set_found_(true);
|
||||||
this->found_ = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -6,100 +6,102 @@ namespace esphome {
|
||||||
namespace captive_portal {
|
namespace captive_portal {
|
||||||
|
|
||||||
const uint8_t INDEX_GZ[] PROGMEM = {
|
const uint8_t INDEX_GZ[] PROGMEM = {
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x5b, 0x8f, 0xdb, 0x36, 0x16, 0x7e, 0xef,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x69, 0x6f, 0xdc, 0x36, 0x1a, 0xfe, 0xde,
|
||||||
0xaf, 0xe0, 0x2a, 0x49, 0x2d, 0x37, 0x23, 0xea, 0x66, 0xf9, 0x2a, 0xa9, 0x48, 0xb2, 0x29, 0x5a, 0x20, 0x69, 0x03,
|
0x5f, 0xc1, 0x2a, 0x49, 0x47, 0xd3, 0x58, 0xd4, 0x35, 0x9a, 0x53, 0x9a, 0xc2, 0xf1, 0xa6, 0x68, 0x81, 0xa4, 0x0d,
|
||||||
0xcc, 0xb4, 0xfb, 0x10, 0x04, 0x18, 0x5a, 0xa2, 0x2c, 0x66, 0x24, 0x4a, 0x15, 0xe9, 0x5b, 0x0c, 0xef, 0x6f, 0xdf,
|
0x60, 0xb7, 0xfd, 0x10, 0x04, 0x30, 0x47, 0xa2, 0x46, 0x8c, 0x25, 0x4a, 0x2b, 0x72, 0xae, 0x0c, 0x66, 0x7f, 0xfb,
|
||||||
0x43, 0x52, 0xf6, 0x38, 0xb3, 0x99, 0x05, 0x52, 0xec, 0x62, 0xd1, 0x4e, 0x26, 0x1c, 0x92, 0x3a, 0xd7, 0x4f, 0x3c,
|
0xbe, 0x24, 0x35, 0xe3, 0xb1, 0x37, 0x5e, 0x6c, 0x8a, 0x2d, 0x8a, 0xd6, 0x71, 0x68, 0x1e, 0xef, 0xf9, 0x88, 0xef,
|
||||||
0x17, 0x2a, 0xfe, 0x5b, 0xde, 0x64, 0x72, 0xdf, 0x52, 0x54, 0xca, 0xba, 0x4a, 0x63, 0x35, 0xa2, 0x8a, 0xf0, 0x55,
|
0x21, 0xc5, 0x5f, 0x67, 0x75, 0x2a, 0x77, 0x0d, 0x45, 0x85, 0xac, 0xca, 0x79, 0xac, 0x46, 0x54, 0x12, 0xbe, 0x4c,
|
||||||
0x42, 0x39, 0xac, 0x28, 0xc9, 0xd3, 0xb8, 0xa6, 0x92, 0xa0, 0xac, 0x24, 0x9d, 0xa0, 0x32, 0xf9, 0xf5, 0xe6, 0x07,
|
0x28, 0x87, 0x15, 0x25, 0xd9, 0x3c, 0xae, 0xa8, 0x24, 0x28, 0x2d, 0x48, 0x2b, 0xa8, 0x4c, 0x7e, 0xb9, 0xf9, 0xde,
|
||||||
0x67, 0x8a, 0xdc, 0x34, 0xae, 0x18, 0xbf, 0x43, 0x1d, 0xad, 0x12, 0x96, 0x35, 0x1c, 0x95, 0x1d, 0x2d, 0x92, 0x9c,
|
0x19, 0x23, 0x77, 0x1e, 0x97, 0x8c, 0xdf, 0xa1, 0x96, 0x96, 0x09, 0x4b, 0x6b, 0x8e, 0x8a, 0x96, 0xe6, 0x49, 0x46,
|
||||||
0x48, 0x32, 0x67, 0x35, 0x59, 0x51, 0x45, 0xa0, 0xd9, 0x38, 0xa9, 0x69, 0xb2, 0x61, 0x74, 0xdb, 0x36, 0x9d, 0x44,
|
0x24, 0x99, 0xb2, 0x8a, 0x2c, 0xa9, 0x22, 0xd0, 0x6c, 0x9c, 0x54, 0x34, 0x59, 0x33, 0xba, 0x69, 0xea, 0x56, 0x22,
|
||||||
0x40, 0x29, 0x29, 0x97, 0x89, 0xb5, 0x65, 0xb9, 0x2c, 0x93, 0x9c, 0x6e, 0x58, 0x46, 0x1d, 0xbd, 0xb8, 0x62, 0x9c,
|
0xa0, 0x94, 0x94, 0xcb, 0xc4, 0xda, 0xb0, 0x4c, 0x16, 0x49, 0x46, 0xd7, 0x2c, 0xa5, 0x8e, 0x5e, 0x5c, 0x30, 0xce,
|
||||||
0x49, 0x46, 0x2a, 0x47, 0x64, 0xa4, 0xa2, 0x89, 0x7f, 0xb5, 0x16, 0xb4, 0xd3, 0x0b, 0xb2, 0x84, 0x35, 0x6f, 0x2c,
|
0x24, 0x23, 0xa5, 0x23, 0x52, 0x52, 0xd2, 0xc4, 0xbf, 0x58, 0x09, 0xda, 0xea, 0x05, 0x59, 0xc0, 0x9a, 0xd7, 0x16,
|
||||||
0x10, 0x29, 0xb2, 0x8e, 0xb5, 0x12, 0x29, 0x7b, 0x93, 0xba, 0xc9, 0xd7, 0x15, 0x4d, 0x5d, 0x97, 0x08, 0xb0, 0x4b,
|
0x88, 0x14, 0x69, 0xcb, 0x1a, 0x89, 0x94, 0xbd, 0x49, 0x55, 0x67, 0xab, 0x92, 0xce, 0x5d, 0x97, 0x08, 0xb0, 0x4b,
|
||||||
0xb8, 0x8c, 0xe7, 0x74, 0x87, 0xa7, 0xb3, 0x68, 0x32, 0x9e, 0xe6, 0x13, 0xfc, 0x51, 0x7c, 0x03, 0x9e, 0xad, 0x6b,
|
0xb8, 0x8c, 0x67, 0x74, 0x8b, 0x87, 0x61, 0x98, 0x06, 0x64, 0x94, 0xe3, 0x8f, 0xe2, 0x2b, 0xf0, 0x6c, 0x55, 0x81,
|
||||||
0x50, 0x87, 0xab, 0x26, 0x23, 0x92, 0x35, 0x1c, 0x0b, 0x4a, 0xba, 0xac, 0x4c, 0x92, 0xc4, 0xfa, 0x5e, 0x90, 0x0d,
|
0x3a, 0x5c, 0xd6, 0x29, 0x91, 0xac, 0xe6, 0x58, 0x50, 0xd2, 0xa6, 0x45, 0x92, 0x24, 0xd6, 0x77, 0x82, 0xac, 0xa9,
|
||||||
0xb5, 0xbe, 0xfd, 0xd6, 0x3e, 0x13, 0xad, 0xa8, 0x7c, 0x5d, 0x51, 0x35, 0x15, 0x2f, 0xf7, 0x37, 0x64, 0xf5, 0x33,
|
0xf5, 0xcd, 0x37, 0xf6, 0x89, 0x68, 0x49, 0xe5, 0xeb, 0x92, 0xaa, 0xa9, 0x78, 0xb5, 0xbb, 0x21, 0xcb, 0x9f, 0xc0,
|
||||||
0x58, 0x6e, 0x5b, 0x44, 0xb0, 0x9c, 0x5a, 0xc3, 0xf7, 0xde, 0x07, 0x2c, 0xe4, 0xbe, 0xa2, 0x38, 0x67, 0xa2, 0xad,
|
0x72, 0xdb, 0x22, 0x82, 0x65, 0xd4, 0xea, 0xbf, 0xf7, 0x3e, 0x60, 0x21, 0x77, 0x25, 0xc5, 0x19, 0x13, 0x4d, 0x49,
|
||||||
0xc8, 0x3e, 0xb1, 0x96, 0x20, 0xf5, 0xce, 0x1a, 0x2e, 0x8a, 0x35, 0xcf, 0x94, 0x70, 0x24, 0x6c, 0x3a, 0x3c, 0x54,
|
0x76, 0x89, 0xb5, 0x00, 0xa9, 0x77, 0x56, 0x7f, 0x96, 0xaf, 0x78, 0xaa, 0x84, 0x23, 0x61, 0xd3, 0xfe, 0xbe, 0xa4,
|
||||||
0x14, 0xcc, 0x4b, 0xde, 0x12, 0x59, 0xe2, 0x9a, 0xec, 0x6c, 0x33, 0x61, 0xdc, 0x0e, 0xbe, 0xb3, 0xe9, 0x73, 0xdf,
|
0x60, 0x5e, 0xf2, 0x96, 0xc8, 0x02, 0x57, 0x64, 0x6b, 0x9b, 0x09, 0xe3, 0x76, 0xf0, 0xad, 0x4d, 0x5f, 0xfa, 0x9e,
|
||||||
0xf3, 0x86, 0x57, 0x7a, 0xf0, 0x86, 0x2e, 0xfc, 0x5d, 0x74, 0x54, 0xae, 0x3b, 0x8e, 0x88, 0x7d, 0x1b, 0xb7, 0x40,
|
0xd7, 0xbf, 0xd0, 0x83, 0xd7, 0x77, 0xe1, 0xef, 0xac, 0xa5, 0x72, 0xd5, 0x72, 0x44, 0xec, 0xdb, 0xb8, 0x01, 0x4a,
|
||||||
0x89, 0xf2, 0xc4, 0xaa, 0xfd, 0x00, 0x7b, 0xde, 0x14, 0xf9, 0x33, 0x1c, 0x44, 0x8e, 0xef, 0xe3, 0xd0, 0xf1, 0xa3,
|
0x94, 0x25, 0x56, 0xe5, 0x07, 0xd8, 0xf3, 0xc6, 0xc8, 0x9f, 0xe0, 0x20, 0x72, 0x7c, 0x1f, 0x87, 0x8e, 0x1f, 0xa5,
|
||||||
0x6c, 0xe2, 0x44, 0xc8, 0x1f, 0xc1, 0x10, 0x04, 0x38, 0x42, 0xde, 0x27, 0x0b, 0x15, 0xac, 0xaa, 0x12, 0x8b, 0x37,
|
0x23, 0x27, 0x42, 0xfe, 0x00, 0x86, 0x20, 0xc0, 0x11, 0xf2, 0x3e, 0x59, 0x28, 0x67, 0x65, 0x99, 0x58, 0xbc, 0xe6,
|
||||||
0x9c, 0x5a, 0x48, 0xc8, 0xae, 0xb9, 0xa3, 0x89, 0x95, 0xad, 0xbb, 0x0e, 0xec, 0x7f, 0xd5, 0x54, 0x4d, 0x07, 0x70,
|
0xd4, 0x42, 0x42, 0xb6, 0xf5, 0x1d, 0x4d, 0xac, 0x74, 0xd5, 0xb6, 0x60, 0xff, 0x55, 0x5d, 0xd6, 0x2d, 0xc0, 0xf5,
|
||||||
0x7d, 0x83, 0x3e, 0xfb, 0xf9, 0x6a, 0x15, 0xb2, 0x23, 0x5c, 0x14, 0x4d, 0x57, 0x27, 0x96, 0x7e, 0x29, 0xf6, 0xd3,
|
0x15, 0x7a, 0xf0, 0xf3, 0xc5, 0x2a, 0x64, 0x4b, 0xb8, 0xc8, 0xeb, 0xb6, 0x4a, 0x2c, 0xfd, 0x50, 0xec, 0xe7, 0x7b,
|
||||||
0x83, 0x3c, 0x22, 0x35, 0x0c, 0x2f, 0x1e, 0x3a, 0x4d, 0xc7, 0x56, 0x8c, 0x27, 0x96, 0x1f, 0x20, 0x7f, 0x0a, 0x6a,
|
0x79, 0x40, 0x6a, 0xe8, 0x9f, 0x1d, 0x3a, 0x75, 0xcb, 0x96, 0x8c, 0x27, 0x96, 0x1f, 0x20, 0x7f, 0x0c, 0x6a, 0x6f,
|
||||||
0x6f, 0x87, 0xc7, 0x33, 0x26, 0x44, 0x61, 0xd2, 0x7b, 0xd9, 0xd8, 0xef, 0x6f, 0x63, 0xb1, 0x59, 0xa1, 0x5d, 0x5d,
|
0xfb, 0x87, 0x13, 0x26, 0x44, 0x61, 0xd2, 0x79, 0x59, 0xdb, 0xef, 0x6f, 0x63, 0xb1, 0x5e, 0xa2, 0x6d, 0x55, 0x72,
|
||||||
0x71, 0x91, 0x58, 0xa5, 0x94, 0xed, 0xdc, 0x75, 0xb7, 0xdb, 0x2d, 0xde, 0x86, 0xb8, 0xe9, 0x56, 0x6e, 0xe0, 0x79,
|
0x91, 0x58, 0x85, 0x94, 0xcd, 0xd4, 0x75, 0x37, 0x9b, 0x0d, 0xde, 0x84, 0xb8, 0x6e, 0x97, 0x6e, 0xe0, 0x79, 0x9e,
|
||||||
0x9e, 0x0b, 0x14, 0x16, 0x32, 0xe7, 0xc3, 0x0a, 0x46, 0x16, 0x2a, 0x29, 0x5b, 0x95, 0x52, 0xcf, 0xd3, 0xa7, 0x07,
|
0x0b, 0x14, 0x16, 0x32, 0xf7, 0xc3, 0x0a, 0x06, 0x16, 0x2a, 0x28, 0x5b, 0x16, 0x52, 0xcf, 0xe7, 0xcf, 0xf7, 0xf4,
|
||||||
0x7a, 0x8c, 0x15, 0x45, 0x7a, 0xfb, 0xe1, 0x42, 0x4b, 0x77, 0xa1, 0x85, 0x7e, 0x7f, 0x81, 0xe6, 0xe0, 0xad, 0x32,
|
0x10, 0x2b, 0x8a, 0xf9, 0xed, 0x87, 0x33, 0x2d, 0xec, 0x4c, 0x0b, 0xfd, 0xee, 0x0c, 0xcd, 0xde, 0x5b, 0x65, 0xd4,
|
||||||
0x6a, 0x42, 0x02, 0x14, 0x20, 0x4f, 0xff, 0x0b, 0x1c, 0x35, 0xef, 0x57, 0xce, 0x83, 0x15, 0xba, 0x58, 0xc1, 0x5f,
|
0x88, 0x04, 0x28, 0x40, 0x9e, 0xfe, 0x17, 0x38, 0x6a, 0xde, 0xad, 0x9c, 0x47, 0x2b, 0x74, 0xb6, 0x82, 0xbf, 0x80,
|
||||||
0xc0, 0x2f, 0xa8, 0xc7, 0xce, 0xec, 0xcc, 0xee, 0xab, 0xc7, 0x1b, 0xdf, 0xbb, 0xdf, 0x50, 0x3c, 0x3f, 0x8e, 0x2f,
|
0x5f, 0x50, 0x0d, 0x9d, 0xc9, 0x89, 0xdd, 0x57, 0xc7, 0x6b, 0xdf, 0xbb, 0xdf, 0x50, 0x3c, 0x3f, 0x0c, 0xcf, 0xd7,
|
||||||
0xd7, 0x4e, 0xf0, 0x9b, 0x22, 0x50, 0xd8, 0x9f, 0x99, 0x9c, 0xa0, 0xf4, 0x7f, 0x1b, 0x93, 0x08, 0x45, 0xfd, 0x4e,
|
0x4e, 0xf0, 0xab, 0x22, 0x50, 0xd8, 0x9f, 0x98, 0x9c, 0xa0, 0xf0, 0x7f, 0x1d, 0x92, 0x08, 0x45, 0xdd, 0x4e, 0xe4,
|
||||||
0xe4, 0xa8, 0xf9, 0x79, 0xa5, 0x34, 0xa1, 0x68, 0x03, 0x54, 0xb5, 0x33, 0x76, 0x22, 0x12, 0xa2, 0xb0, 0x37, 0x09,
|
0xa8, 0xf9, 0x69, 0xa5, 0x34, 0xa1, 0x68, 0x0d, 0x54, 0x95, 0x33, 0x74, 0x22, 0x12, 0xa2, 0xb0, 0x33, 0x09, 0x66,
|
||||||
0x66, 0xb0, 0x3d, 0x06, 0xe6, 0x8b, 0x3d, 0x27, 0xfc, 0x34, 0x50, 0x30, 0xcf, 0x2d, 0xeb, 0x1e, 0x83, 0xe6, 0x12,
|
0xb0, 0x3d, 0x04, 0xe6, 0xb3, 0x3d, 0x27, 0xfc, 0xd4, 0x53, 0x30, 0x4f, 0x2d, 0xeb, 0x1e, 0x83, 0xfa, 0x1c, 0x03,
|
||||||
0x03, 0xfc, 0xb1, 0x81, 0x33, 0x67, 0x59, 0x80, 0x11, 0x95, 0x59, 0x69, 0x5b, 0x2e, 0x44, 0x5e, 0xc1, 0x56, 0x10,
|
0xfc, 0xb1, 0x86, 0x3b, 0x67, 0x59, 0x80, 0x11, 0x95, 0x69, 0x61, 0x5b, 0x2e, 0x44, 0x5e, 0xce, 0x96, 0x10, 0x15,
|
||||||
0x15, 0x0d, 0xb7, 0x86, 0x58, 0x96, 0x94, 0xdb, 0x27, 0x56, 0xc5, 0x48, 0xf5, 0x13, 0xfb, 0xe1, 0x13, 0x39, 0x3c,
|
0x35, 0xb7, 0xfa, 0x58, 0x16, 0x94, 0xdb, 0x47, 0x56, 0xc5, 0x48, 0xf5, 0x89, 0xfd, 0xf8, 0x44, 0xf6, 0xf7, 0xa7,
|
||||||
0x9c, 0xe3, 0x43, 0x32, 0x09, 0x71, 0x28, 0xb1, 0x8a, 0xe8, 0xab, 0xf3, 0xee, 0xb2, 0xc9, 0xf7, 0x8f, 0x84, 0x4e,
|
0xf8, 0x90, 0x4c, 0x42, 0x1c, 0x4a, 0xac, 0x22, 0xfa, 0xe2, 0xb4, 0xbb, 0xa8, 0xb3, 0xdd, 0x13, 0xa1, 0x53, 0xf8,
|
||||||
0xe9, 0x9b, 0xb8, 0x61, 0x9c, 0xd3, 0xee, 0x86, 0xee, 0xe0, 0x1d, 0xfe, 0x83, 0xfd, 0xc0, 0xd0, 0xcf, 0x54, 0x6e,
|
0x26, 0x6e, 0x18, 0xe7, 0xb4, 0xbd, 0xa1, 0x5b, 0x78, 0x86, 0x6f, 0x2f, 0xaf, 0xd0, 0x65, 0x96, 0xb5, 0x54, 0x88,
|
||||||
0x9b, 0xee, 0x4e, 0xcc, 0x91, 0xf5, 0xdc, 0x88, 0x5b, 0xa8, 0xa8, 0x61, 0x20, 0x9b, 0xb4, 0x02, 0x8b, 0x0a, 0x72,
|
0x29, 0xb2, 0x5e, 0x4a, 0x88, 0x91, 0xf4, 0x7f, 0x97, 0xe5, 0x3f, 0x90, 0xf5, 0x1b, 0xfb, 0x9e, 0xa1, 0x9f, 0xa8,
|
||||||
0x82, 0xed, 0x0f, 0x21, 0x7e, 0xda, 0x7b, 0x4b, 0xf8, 0xc9, 0xb9, 0xdb, 0x38, 0x67, 0x1b, 0x94, 0x55, 0x10, 0xf5,
|
0xdc, 0xd4, 0xed, 0x5d, 0x27, 0x4d, 0x99, 0x36, 0x53, 0x11, 0xd8, 0x82, 0x9d, 0xa4, 0x11, 0x58, 0x94, 0x90, 0x5f,
|
||||||
0x70, 0xfc, 0x8d, 0x28, 0x0b, 0xf5, 0x47, 0xbd, 0xe1, 0x19, 0x70, 0xdf, 0x25, 0xd6, 0x17, 0xa2, 0xfa, 0xe5, 0xfe,
|
0x6c, 0xbf, 0x0f, 0x7a, 0x9a, 0x7b, 0xaf, 0xf8, 0x11, 0xa8, 0xdb, 0x38, 0x63, 0x6b, 0x94, 0x96, 0x90, 0x41, 0x20,
|
||||||
0xa7, 0xdc, 0x1e, 0x08, 0x88, 0xe7, 0xc1, 0x10, 0x6f, 0x48, 0xb5, 0xa6, 0x28, 0x41, 0xb2, 0x64, 0xe2, 0xde, 0xc0,
|
0x94, 0x8c, 0x28, 0x0b, 0x75, 0x61, 0x53, 0xf3, 0x14, 0xb8, 0xef, 0x12, 0xeb, 0x33, 0x19, 0xe2, 0xd5, 0xee, 0xc7,
|
||||||
0xc5, 0xa3, 0x6c, 0xad, 0xb8, 0x03, 0xae, 0x02, 0x1e, 0x0b, 0x7b, 0x68, 0x9d, 0x22, 0x2b, 0x26, 0x26, 0xef, 0x59,
|
0xcc, 0xee, 0x09, 0xc8, 0x0d, 0xbd, 0x3e, 0x5e, 0x93, 0x72, 0x45, 0x51, 0x82, 0x64, 0xc1, 0xc4, 0xbd, 0x81, 0xb3,
|
||||||
0x4f, 0xac, 0x07, 0x16, 0x39, 0x15, 0x2d, 0xa4, 0x75, 0x1f, 0x81, 0x4f, 0x0f, 0xc2, 0xe6, 0xb8, 0x03, 0xed, 0xc3,
|
0x27, 0xd9, 0x1a, 0x71, 0x07, 0x5c, 0x39, 0x1c, 0x0b, 0xbb, 0x6f, 0x1d, 0xa3, 0x34, 0x26, 0x26, 0x87, 0x5a, 0xcf,
|
||||||
0xe3, 0x79, 0x33, 0x16, 0x2d, 0xe1, 0x0f, 0x19, 0x95, 0x81, 0xea, 0xa0, 0x43, 0xb2, 0x82, 0x99, 0x3a, 0xed, 0x40,
|
0xac, 0x47, 0x16, 0x39, 0x25, 0xcd, 0xa5, 0x75, 0x1f, 0xcd, 0xcf, 0xf7, 0xc2, 0xe6, 0xb8, 0x05, 0xed, 0xfd, 0xc3,
|
||||||
0x74, 0x56, 0xe8, 0x92, 0xd3, 0xf4, 0xe9, 0xa1, 0x03, 0x89, 0x2a, 0x07, 0x9d, 0x25, 0xc6, 0x2e, 0x40, 0x93, 0xde,
|
0x69, 0x33, 0x16, 0x0d, 0xe1, 0x8f, 0x19, 0x95, 0x81, 0x2a, 0x68, 0x20, 0xf1, 0xc1, 0x4c, 0x45, 0x0e, 0x10, 0x9d,
|
||||||
0x1e, 0x87, 0xf7, 0x7e, 0xfc, 0xbe, 0xa6, 0xdd, 0xfe, 0x9a, 0x56, 0x34, 0x93, 0x4d, 0x67, 0x5b, 0x4f, 0x40, 0x0b,
|
0x14, 0xba, 0xe4, 0x38, 0x7d, 0xbe, 0x67, 0x20, 0x51, 0xe5, 0xb3, 0x93, 0xc4, 0xd8, 0x05, 0x68, 0xe6, 0xb7, 0x87,
|
||||||
0xbc, 0x7e, 0xed, 0xf0, 0x8f, 0x37, 0x6f, 0xdf, 0x24, 0x8d, 0xcd, 0x86, 0x57, 0x8f, 0x51, 0xab, 0x0c, 0xff, 0x1e,
|
0xfe, 0xbd, 0x1f, 0xff, 0x5c, 0xd1, 0x76, 0x77, 0x4d, 0x4b, 0x9a, 0xca, 0xba, 0xb5, 0xad, 0x67, 0xa0, 0x05, 0xae,
|
||||||
0x32, 0xfc, 0x3f, 0x93, 0x81, 0xca, 0xf1, 0x83, 0x0f, 0xc0, 0xaa, 0xfd, 0xbd, 0xbd, 0x4f, 0xf4, 0x2a, 0x18, 0x9f,
|
0x92, 0x76, 0xf8, 0x87, 0x9b, 0xb7, 0x6f, 0x92, 0xda, 0x6e, 0xfb, 0x17, 0x4f, 0x51, 0xab, 0x6a, 0xf1, 0x1e, 0xaa,
|
||||||
0x43, 0x40, 0x5f, 0x29, 0x0f, 0x9d, 0x71, 0x34, 0x3c, 0x82, 0x7e, 0xb0, 0x00, 0xec, 0xd6, 0xb9, 0x1a, 0x72, 0xb6,
|
0xc5, 0xbf, 0x92, 0x9e, 0xaa, 0x17, 0xbd, 0x0f, 0xc0, 0xaa, 0xfd, 0xbd, 0xbd, 0x2f, 0x1a, 0x2a, 0xb0, 0x5f, 0x42,
|
||||||
0x4a, 0x9b, 0xe9, 0x77, 0x87, 0x65, 0xb3, 0x73, 0x04, 0xfb, 0xc4, 0xf8, 0x6a, 0xce, 0x78, 0x49, 0x3b, 0x26, 0x8f,
|
0x72, 0xb8, 0x50, 0x1e, 0x3a, 0xc3, 0xa8, 0x7f, 0x00, 0xfd, 0x60, 0x01, 0xd8, 0xad, 0xf3, 0x3e, 0xe4, 0x7f, 0x95,
|
||||||
0x60, 0x2e, 0xa4, 0xfd, 0x76, 0x2d, 0x0f, 0x2d, 0xc9, 0x73, 0xf5, 0x24, 0x6a, 0x77, 0x8b, 0x02, 0x8a, 0x84, 0xa2,
|
0x82, 0xe7, 0xdf, 0xee, 0x17, 0xf5, 0xd6, 0x11, 0xec, 0x13, 0xe3, 0xcb, 0x29, 0xe3, 0x05, 0x6d, 0x99, 0x3c, 0x80,
|
||||||
0xa4, 0x73, 0x9f, 0xd6, 0x47, 0xf3, 0x5c, 0xe7, 0x83, 0xf9, 0x2c, 0x7a, 0x76, 0x54, 0x07, 0xee, 0x20, 0xe1, 0x65,
|
0xb9, 0x50, 0x42, 0x9a, 0x95, 0xdc, 0x37, 0x24, 0xcb, 0xd4, 0x49, 0xd4, 0x6c, 0x67, 0x39, 0x14, 0x1c, 0x45, 0x49,
|
||||||
0x39, 0xa4, 0x62, 0x2b, 0x3e, 0xcf, 0xc0, 0x70, 0xda, 0x19, 0xa6, 0x82, 0xd4, 0xac, 0xda, 0xcf, 0x05, 0x64, 0x26,
|
0xa7, 0x3e, 0xad, 0x0e, 0xe6, 0x5c, 0xe7, 0x96, 0xe9, 0x24, 0x7a, 0x71, 0x50, 0x17, 0x6e, 0x2f, 0xe1, 0x61, 0x39,
|
||||||
0x07, 0xaa, 0x07, 0x2b, 0x8e, 0xcb, 0xb5, 0x94, 0x0d, 0x07, 0xdd, 0x5d, 0x4e, 0xbb, 0xb9, 0xb7, 0x30, 0x13, 0xa7,
|
0xa4, 0x64, 0x4b, 0x3e, 0x4d, 0xc1, 0x70, 0xda, 0x1a, 0xa6, 0x9c, 0x54, 0xac, 0xdc, 0x4d, 0x05, 0x64, 0x39, 0x07,
|
||||||
0x23, 0x39, 0x5b, 0x8b, 0x39, 0x0e, 0x3b, 0x5a, 0x2f, 0x96, 0x24, 0xbb, 0x5b, 0x75, 0xcd, 0x9a, 0xe7, 0x4e, 0xa6,
|
0x2a, 0x11, 0xcb, 0x0f, 0x8b, 0x95, 0x94, 0x35, 0x07, 0xdd, 0x6d, 0x46, 0xdb, 0xa9, 0x37, 0x33, 0x13, 0xa7, 0x25,
|
||||||
0x32, 0xe7, 0xfc, 0x89, 0x5f, 0x90, 0x90, 0x66, 0x8b, 0x7e, 0x55, 0x14, 0xc5, 0x02, 0xa0, 0xa0, 0x8e, 0xc9, 0x44,
|
0x19, 0x5b, 0x89, 0x29, 0x0e, 0x5b, 0x5a, 0xcd, 0x16, 0x24, 0xbd, 0x5b, 0xb6, 0xf5, 0x8a, 0x67, 0x4e, 0xaa, 0xb2,
|
||||||
0xf3, 0x00, 0x8f, 0x14, 0xdb, 0x85, 0x99, 0x38, 0x50, 0x1b, 0xc6, 0x46, 0x48, 0xeb, 0xcf, 0x16, 0x27, 0x77, 0xbc,
|
0xf0, 0xf4, 0x99, 0x9f, 0x93, 0x90, 0xa6, 0xb3, 0x6e, 0x95, 0xe7, 0xf9, 0x0c, 0xa0, 0xa0, 0x8e, 0xc9, 0x6a, 0xd3,
|
||||||
0x05, 0xa4, 0x64, 0x01, 0x42, 0x5a, 0x88, 0x47, 0x30, 0xf3, 0x58, 0x13, 0xc6, 0x2f, 0xad, 0x57, 0xc7, 0x64, 0xd1,
|
0x00, 0x0f, 0x14, 0xdb, 0x99, 0x99, 0x38, 0x50, 0x1b, 0xc6, 0x46, 0x28, 0x11, 0x2f, 0x66, 0x47, 0x77, 0xbc, 0x19,
|
||||||
0x97, 0x14, 0x80, 0x45, 0xab, 0xd1, 0x85, 0x65, 0x01, 0x45, 0xc3, 0x14, 0xc6, 0x79, 0x30, 0xf6, 0xda, 0xdd, 0x11,
|
0xa4, 0x77, 0x01, 0x42, 0x1a, 0x88, 0x6d, 0x30, 0xf3, 0x50, 0x11, 0xc6, 0xcf, 0xad, 0x57, 0xd7, 0x64, 0xd6, 0x95,
|
||||||
0xf7, 0x07, 0xe4, 0x70, 0xa2, 0x2e, 0x2a, 0xba, 0x5b, 0x7c, 0x5c, 0x0b, 0xc9, 0x8a, 0xbd, 0xd3, 0x17, 0xd6, 0x39,
|
0x27, 0x80, 0x45, 0xab, 0xd1, 0x45, 0x6a, 0x06, 0x05, 0xc8, 0x14, 0xd9, 0x69, 0x30, 0xf4, 0x9a, 0xed, 0x01, 0x77,
|
||||||
0x1c, 0x16, 0x28, 0xa8, 0x4b, 0x20, 0xa5, 0x94, 0x2f, 0xb4, 0x0e, 0x87, 0x49, 0x5a, 0x8b, 0x1e, 0xa7, 0xb3, 0x18,
|
0x17, 0x64, 0x7f, 0xa4, 0xce, 0x4b, 0xba, 0x9d, 0x7d, 0x5c, 0x09, 0xc9, 0xf2, 0x9d, 0xd3, 0x15, 0xe9, 0x29, 0x5c,
|
||||||
0x7d, 0x40, 0x3f, 0x97, 0xf5, 0x9f, 0xa8, 0xd5, 0x59, 0x3c, 0xd4, 0xa4, 0x83, 0x44, 0xef, 0x2c, 0x1b, 0xc0, 0xb4,
|
0x16, 0x28, 0xce, 0x0b, 0x20, 0xa5, 0x94, 0xcf, 0xb4, 0x0e, 0x87, 0x49, 0x5a, 0x89, 0x0e, 0xa7, 0x93, 0x18, 0x7d,
|
||||||
0x9e, 0x3b, 0x13, 0x78, 0x57, 0xfd, 0x96, 0x12, 0x06, 0x9e, 0x83, 0x99, 0xba, 0x5e, 0x9e, 0xf0, 0xf6, 0xdb, 0x1d,
|
0x41, 0x1f, 0xca, 0xfa, 0x6f, 0xd4, 0xea, 0x2e, 0xee, 0x2b, 0xd2, 0x42, 0xd1, 0x70, 0x16, 0x35, 0x60, 0x5a, 0x4d,
|
||||||
0x12, 0x4d, 0xc5, 0xf2, 0x9e, 0x4e, 0x93, 0x20, 0xef, 0x0c, 0x8f, 0x0f, 0xaf, 0x1b, 0xa9, 0xbd, 0x13, 0xd4, 0xa3,
|
0x9d, 0x11, 0x3c, 0xab, 0x6e, 0x4b, 0x09, 0x03, 0xcf, 0xc1, 0x4c, 0x5d, 0x7b, 0x8f, 0x78, 0xfb, 0xcd, 0x16, 0x89,
|
||||||
0x62, 0x4a, 0x7c, 0xef, 0x0b, 0x6f, 0x24, 0x2f, 0x8a, 0x60, 0x59, 0x9c, 0x91, 0x52, 0x65, 0xef, 0xc8, 0xfa, 0x53,
|
0xba, 0x64, 0x59, 0x47, 0xa7, 0x49, 0x90, 0x77, 0x82, 0xc7, 0x87, 0xc7, 0x8d, 0xd4, 0xde, 0x11, 0xea, 0x41, 0x3e,
|
||||||
0x11, 0x8c, 0x40, 0xc0, 0xe9, 0xdd, 0xc0, 0xfc, 0xc8, 0x74, 0x58, 0x1c, 0x2e, 0xa4, 0xe8, 0xa3, 0x3a, 0x5f, 0x77,
|
0x26, 0xbe, 0xf7, 0x99, 0x27, 0x92, 0xe5, 0x79, 0xb0, 0xc8, 0x4f, 0x48, 0xa9, 0x12, 0x7a, 0x60, 0xdd, 0xad, 0x08,
|
||||||
0x95, 0x6d, 0x7d, 0xe1, 0xe8, 0x3e, 0x0b, 0x5f, 0xdd, 0x97, 0xa5, 0xc1, 0xe3, 0x65, 0x69, 0x80, 0x54, 0x23, 0xf3,
|
0x06, 0x20, 0xe0, 0xf8, 0x6c, 0x60, 0x7e, 0x60, 0x3a, 0x2c, 0xf6, 0x67, 0x52, 0xf4, 0x55, 0x9d, 0xae, 0xda, 0xd2,
|
||||||
0xb2, 0xd9, 0x25, 0x03, 0x5d, 0x20, 0x46, 0xf0, 0x3b, 0x78, 0x16, 0xbe, 0x06, 0xfe, 0xff, 0x4a, 0xbd, 0xf9, 0xc3,
|
0xb6, 0x3e, 0x73, 0x75, 0x5f, 0x84, 0x57, 0xf7, 0x25, 0xae, 0xf7, 0x74, 0x89, 0xeb, 0x21, 0xd5, 0x14, 0xbd, 0xaa,
|
||||||
0xc5, 0xe6, 0x2b, 0x2a, 0xcd, 0x57, 0x56, 0x19, 0xe3, 0x9d, 0x72, 0x1e, 0x66, 0x50, 0x4e, 0x18, 0x16, 0x6c, 0xe5,
|
0xb7, 0x49, 0x4f, 0x17, 0x9b, 0x01, 0xfc, 0xf6, 0x5e, 0x84, 0xaf, 0x81, 0xff, 0xff, 0x52, 0xbb, 0x7e, 0x77, 0xe1,
|
||||||
0xff, 0x2f, 0xa0, 0xfd, 0x77, 0x1c, 0xc3, 0x17, 0xfe, 0x14, 0xcf, 0x90, 0x1e, 0x0c, 0x44, 0x38, 0x9c, 0xa2, 0xc9,
|
0xfa, 0x82, 0xaa, 0xf5, 0x85, 0x15, 0xcb, 0x78, 0xa7, 0x9c, 0x87, 0x19, 0x94, 0x26, 0x86, 0x05, 0x5b, 0xfa, 0x7f,
|
||||||
0xab, 0x11, 0x1e, 0xf9, 0x48, 0xb5, 0x30, 0x63, 0x34, 0x81, 0x7e, 0x0f, 0xf9, 0x63, 0x1c, 0x4e, 0x60, 0x03, 0x05,
|
0x04, 0xb4, 0xff, 0x89, 0x63, 0x78, 0xe9, 0x8f, 0xf1, 0x04, 0xe9, 0xc1, 0x40, 0x84, 0xc3, 0x31, 0x1a, 0x5d, 0x0d,
|
||||||
0x3e, 0x8e, 0xde, 0x04, 0x21, 0x1e, 0x47, 0x40, 0x15, 0x78, 0x38, 0x0c, 0x90, 0xa1, 0x1d, 0xe3, 0x00, 0xc4, 0x29,
|
0xf0, 0xc0, 0x47, 0xaa, 0x1d, 0x1a, 0xa2, 0x11, 0x1e, 0x03, 0xc1, 0x10, 0x87, 0x23, 0xd8, 0x40, 0x81, 0x8f, 0xa3,
|
||||||
0x92, 0xb0, 0x06, 0xa0, 0xb3, 0x10, 0x7b, 0x13, 0x10, 0x37, 0xc6, 0xde, 0x0c, 0x4f, 0xc7, 0x68, 0x8a, 0x27, 0x00,
|
0x37, 0x41, 0x88, 0x87, 0x11, 0x50, 0x05, 0x1e, 0x0e, 0x03, 0x64, 0x68, 0x87, 0x38, 0x00, 0x71, 0x8a, 0x24, 0xac,
|
||||||
0x1d, 0x1e, 0x45, 0x95, 0x13, 0x61, 0x1f, 0xb6, 0xc3, 0x31, 0x99, 0xe2, 0x51, 0x88, 0xf4, 0x60, 0xe0, 0x98, 0x80,
|
0x00, 0xe8, 0x34, 0xc4, 0xde, 0x08, 0xc4, 0x0d, 0xb1, 0x37, 0xc1, 0xe3, 0x21, 0x1a, 0xe3, 0x11, 0x40, 0x87, 0x07,
|
||||||
0x08, 0x07, 0x7b, 0xfe, 0x9b, 0x10, 0x07, 0x13, 0xd0, 0x3b, 0x1a, 0xbd, 0x00, 0xb1, 0xb3, 0x11, 0x32, 0xa3, 0x81,
|
0x51, 0xe9, 0x44, 0xd8, 0x87, 0xed, 0x70, 0x48, 0xc6, 0x78, 0x10, 0x22, 0x3d, 0x18, 0x38, 0x46, 0x20, 0xc2, 0xc1,
|
||||||
0x17, 0x14, 0x44, 0x8f, 0x81, 0x16, 0xfc, 0x75, 0x41, 0x03, 0x48, 0x7c, 0x14, 0xe2, 0x19, 0xc4, 0xae, 0xaf, 0xf8,
|
0x9e, 0xff, 0x26, 0xc4, 0xc1, 0x08, 0xf4, 0x0e, 0x06, 0x97, 0x20, 0x76, 0x32, 0x40, 0x66, 0x34, 0xf0, 0x82, 0x82,
|
||||||
0xcd, 0x68, 0x70, 0xf3, 0x7d, 0xe4, 0xfd, 0x61, 0xcc, 0xc2, 0xbf, 0x2e, 0x66, 0xbe, 0x42, 0x00, 0xa6, 0xa0, 0x1b,
|
0xe8, 0x29, 0xd0, 0x82, 0xbf, 0x2f, 0x68, 0x00, 0x89, 0x8f, 0x42, 0x3c, 0x81, 0xd8, 0xf5, 0x15, 0xbf, 0x19, 0x0d,
|
||||||
0xe4, 0x20, 0x3d, 0x18, 0xdd, 0xc0, 0x3c, 0x7d, 0x35, 0x43, 0x53, 0xe0, 0x1a, 0x4f, 0xd1, 0x0c, 0x45, 0x0a, 0x5d,
|
0x6e, 0xbe, 0x8f, 0xbc, 0xdf, 0x8d, 0x59, 0xf8, 0xf7, 0xc5, 0xcc, 0x57, 0x08, 0xc0, 0x14, 0x74, 0x83, 0x1c, 0xa4,
|
||||||
0x60, 0x1f, 0x19, 0x26, 0x07, 0x98, 0xbe, 0x12, 0xc6, 0xd1, 0x9f, 0x18, 0xc6, 0xc7, 0x7c, 0xfa, 0x13, 0xbb, 0xf4,
|
0x07, 0xa3, 0x1b, 0x98, 0xc7, 0x57, 0x13, 0x34, 0x06, 0xae, 0xe1, 0x18, 0x4d, 0x50, 0xa4, 0xd0, 0x05, 0xf6, 0x81,
|
||||||
0xff, 0x48, 0x41, 0xd0, 0x8e, 0xe9, 0x36, 0x2c, 0x76, 0xcd, 0x95, 0x5e, 0x75, 0x51, 0x70, 0x43, 0x87, 0x6e, 0x04,
|
0x61, 0x72, 0x80, 0xe9, 0x0b, 0x61, 0x1c, 0xfc, 0x85, 0x61, 0x7c, 0xca, 0xa7, 0xbf, 0xb0, 0x4b, 0x7f, 0x46, 0x0a,
|
||||||
0x2e, 0xf9, 0x3e, 0x62, 0x79, 0x52, 0xfa, 0xe9, 0x67, 0xdd, 0x39, 0x50, 0xfa, 0x69, 0xac, 0xcb, 0x79, 0x7a, 0x53,
|
0x82, 0x76, 0x4c, 0xb7, 0x61, 0xb1, 0x6b, 0x3e, 0x0f, 0xa8, 0x2e, 0x0a, 0xde, 0xf6, 0xa1, 0x1b, 0x99, 0xc7, 0x85,
|
||||||
0x52, 0xf4, 0xfa, 0xfa, 0x1d, 0xdc, 0xca, 0xaa, 0x0a, 0xf1, 0x66, 0x0b, 0x97, 0xbf, 0x3d, 0x92, 0x8d, 0xba, 0xce,
|
0x8f, 0x58, 0x96, 0x40, 0x57, 0x3f, 0x3f, 0x6b, 0xf5, 0x81, 0xd0, 0x3f, 0x1e, 0xc1, 0xec, 0x41, 0xe3, 0x6e, 0xce,
|
||||||
0x73, 0xe8, 0x15, 0xd5, 0x14, 0xee, 0x0d, 0xa8, 0x6f, 0x16, 0x30, 0xc6, 0xf1, 0xb2, 0x4b, 0xdf, 0x55, 0x94, 0x08,
|
0x74, 0xa5, 0x9f, 0xdf, 0x14, 0x14, 0xbd, 0xbe, 0x7e, 0x07, 0x2f, 0x7f, 0x65, 0x89, 0x78, 0xbd, 0x81, 0x77, 0xcc,
|
||||||
0x8a, 0x56, 0x6c, 0x43, 0x11, 0x93, 0xd0, 0x07, 0xd4, 0x14, 0x49, 0xa6, 0x86, 0x33, 0xa3, 0xa6, 0x83, 0x9e, 0x56,
|
0x1d, 0x92, 0xb5, 0xfa, 0x6a, 0xc0, 0xa1, 0x8d, 0x54, 0x53, 0x78, 0x3d, 0x41, 0x5d, 0x1f, 0x81, 0x31, 0x8e, 0x17,
|
||||||
0x2b, 0x31, 0xdd, 0x30, 0x58, 0x02, 0x62, 0xd2, 0xbe, 0xed, 0x8d, 0xcb, 0xd0, 0x58, 0x75, 0x4d, 0xa5, 0x84, 0x8e,
|
0xed, 0xfc, 0x5d, 0x49, 0x89, 0xa0, 0x68, 0xc9, 0xd6, 0x14, 0x31, 0x09, 0x2d, 0x42, 0x45, 0x91, 0x64, 0x6a, 0x38,
|
||||||
0x41, 0x59, 0x15, 0xa6, 0xb1, 0xba, 0x76, 0x22, 0xa2, 0x2f, 0x06, 0x89, 0xbb, 0x65, 0x05, 0x53, 0x97, 0xf9, 0x34,
|
0x31, 0x6a, 0x3a, 0x68, 0x77, 0xb5, 0x12, 0xd3, 0x28, 0x83, 0x25, 0x20, 0x66, 0xde, 0x75, 0xc4, 0x71, 0x11, 0x1a,
|
||||||
0xd6, 0xad, 0xa2, 0x92, 0xa0, 0xba, 0x15, 0xf3, 0xe5, 0x41, 0xcf, 0x2a, 0xca, 0x57, 0x70, 0x9b, 0x84, 0x77, 0x01,
|
0xab, 0xae, 0xa9, 0x94, 0xd0, 0x4c, 0x28, 0xab, 0xc2, 0x79, 0xac, 0xde, 0x6e, 0x11, 0xd1, 0xef, 0x0c, 0x89, 0xbb,
|
||||||
0xcd, 0x43, 0x46, 0xcb, 0xa6, 0x82, 0xe6, 0x24, 0xb9, 0xbe, 0xfe, 0xe9, 0xef, 0xea, 0x33, 0x85, 0x32, 0xe1, 0xcc,
|
0x61, 0x39, 0x53, 0xdf, 0x0c, 0xe6, 0xb1, 0xee, 0x22, 0x95, 0x04, 0xd5, 0xc8, 0x98, 0x0f, 0x1c, 0x7a, 0x56, 0x52,
|
||||||
0x09, 0x7d, 0xbe, 0x61, 0x54, 0x93, 0x9e, 0x6f, 0x3c, 0x32, 0x1f, 0x1c, 0x5a, 0xe8, 0xd3, 0xc1, 0xbf, 0xfc, 0x33,
|
0xbe, 0x84, 0x97, 0x56, 0x78, 0x4c, 0xd0, 0x57, 0xa4, 0xb4, 0xa8, 0x4b, 0xe8, 0x5b, 0x92, 0xeb, 0xeb, 0x1f, 0xff,
|
||||||
0x29, 0xef, 0x4e, 0x9b, 0xbd, 0x24, 0xfd, 0x5f, 0x37, 0x9d, 0x86, 0x49, 0xac, 0x97, 0x35, 0x93, 0xe9, 0x35, 0x18,
|
0xa1, 0xbe, 0x86, 0x28, 0x13, 0x4e, 0x9c, 0xf0, 0x0a, 0x60, 0x18, 0xd5, 0xa4, 0xe3, 0x1b, 0x0e, 0xcc, 0x77, 0x8d,
|
||||||
0x18, 0xbb, 0xe6, 0x01, 0x38, 0xa7, 0x1c, 0x30, 0xb4, 0x65, 0xcf, 0x03, 0x60, 0xff, 0x72, 0xf3, 0x02, 0xfd, 0xda,
|
0x06, 0x5a, 0x78, 0xf0, 0x2f, 0x7b, 0x20, 0xe5, 0xdd, 0x71, 0xb3, 0x93, 0xa4, 0xff, 0xeb, 0x7e, 0xd4, 0x30, 0x89,
|
||||||
0xc2, 0x09, 0xa6, 0x06, 0x7b, 0xed, 0x65, 0x4d, 0x65, 0xd9, 0xe4, 0xc9, 0xbb, 0x5f, 0xae, 0x6f, 0xce, 0x1e, 0xaf,
|
0xd5, 0xa2, 0x62, 0x72, 0x7e, 0x0d, 0x06, 0xc6, 0xae, 0x39, 0x00, 0xe7, 0x94, 0x03, 0x86, 0xb6, 0xe8, 0x78, 0x00,
|
||||||
0x35, 0x11, 0xa2, 0x3c, 0x33, 0x1f, 0x42, 0xd6, 0x95, 0x64, 0x2d, 0xe9, 0xa4, 0x16, 0xeb, 0xa8, 0x10, 0x38, 0x79,
|
0xec, 0x9f, 0x6f, 0x2e, 0xd1, 0x2f, 0x0d, 0x5c, 0x6e, 0x6a, 0xb0, 0xd7, 0x5e, 0x56, 0x54, 0x16, 0x75, 0x96, 0xbc,
|
||||||
0xa4, 0x9f, 0x17, 0xac, 0xa2, 0xc6, 0xa9, 0x9e, 0xd1, 0x4d, 0xd1, 0x97, 0x6c, 0x3c, 0xe9, 0x7e, 0x60, 0xa5, 0x6b,
|
0xfb, 0xf9, 0xfa, 0xe6, 0xe4, 0xf1, 0x4a, 0x13, 0x21, 0xca, 0x53, 0xf3, 0xbd, 0x65, 0x55, 0x4a, 0xd6, 0x90, 0x56,
|
||||||
0x4e, 0x89, 0x6b, 0x8e, 0x8c, 0xab, 0x3f, 0x13, 0xfd, 0x0b, 0x65, 0x37, 0xa3, 0x8e, 0x36, 0x12, 0x00, 0x00};
|
0x6a, 0xb1, 0x8e, 0x8a, 0x8e, 0xa3, 0x47, 0xfa, 0x3c, 0x67, 0x25, 0x35, 0x4e, 0x75, 0x8c, 0xee, 0x1c, 0x7d, 0xce,
|
||||||
|
0xc6, 0xa3, 0xee, 0x47, 0x56, 0xba, 0xe6, 0x02, 0xb9, 0xe6, 0x36, 0xb9, 0xfa, 0x6b, 0xd4, 0xbf, 0x01, 0x14, 0xee,
|
||||||
|
0xbc, 0x64, 0x9d, 0x12, 0x00, 0x00};
|
||||||
|
|
||||||
} // namespace captive_portal
|
} // namespace captive_portal
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -12,7 +12,7 @@ static const char *const TAG = "captive_portal";
|
||||||
void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
|
void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
|
||||||
AsyncResponseStream *stream = request->beginResponseStream("application/json");
|
AsyncResponseStream *stream = request->beginResponseStream("application/json");
|
||||||
stream->addHeader("cache-control", "public, max-age=0, must-revalidate");
|
stream->addHeader("cache-control", "public, max-age=0, must-revalidate");
|
||||||
stream->printf(R"({"name":"%s","aps":[{})", App.get_name().c_str());
|
stream->printf(R"({"mac":"%s","name":"%s","aps":[{})", get_mac_address_pretty().c_str(), App.get_name().c_str());
|
||||||
|
|
||||||
for (auto &scan : wifi::global_wifi_component->get_scan_result()) {
|
for (auto &scan : wifi::global_wifi_component->get_scan_result()) {
|
||||||
if (scan.get_is_hidden())
|
if (scan.get_is_hidden())
|
||||||
|
|
|
@ -3,38 +3,50 @@ import esphome.codegen as cg
|
||||||
# import cpp_generator as cpp
|
# import cpp_generator as cpp
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, time
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_ON_TIME,
|
||||||
CONF_ON_VALUE,
|
CONF_ON_VALUE,
|
||||||
|
CONF_TIME_ID,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
CONF_DATE,
|
CONF_DATE,
|
||||||
|
CONF_TIME,
|
||||||
CONF_YEAR,
|
CONF_YEAR,
|
||||||
CONF_MONTH,
|
CONF_MONTH,
|
||||||
CONF_DAY,
|
CONF_DAY,
|
||||||
|
CONF_SECOND,
|
||||||
|
CONF_HOUR,
|
||||||
|
CONF_MINUTE,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.cpp_helpers import setup_entity
|
from esphome.cpp_helpers import setup_entity
|
||||||
|
|
||||||
|
|
||||||
CODEOWNERS = ["@rfdarter"]
|
CODEOWNERS = ["@rfdarter", "@jesserockz"]
|
||||||
|
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
datetime_ns = cg.esphome_ns.namespace("datetime")
|
datetime_ns = cg.esphome_ns.namespace("datetime")
|
||||||
DateTimeBase = datetime_ns.class_("DateTimeBase", cg.EntityBase)
|
DateTimeBase = datetime_ns.class_("DateTimeBase", cg.EntityBase)
|
||||||
DateEntity = datetime_ns.class_("DateEntity", DateTimeBase)
|
DateEntity = datetime_ns.class_("DateEntity", DateTimeBase)
|
||||||
|
TimeEntity = datetime_ns.class_("TimeEntity", DateTimeBase)
|
||||||
|
|
||||||
# Actions
|
# Actions
|
||||||
DateSetAction = datetime_ns.class_("DateSetAction", automation.Action)
|
DateSetAction = datetime_ns.class_("DateSetAction", automation.Action)
|
||||||
|
TimeSetAction = datetime_ns.class_("TimeSetAction", automation.Action)
|
||||||
|
|
||||||
DateTimeStateTrigger = datetime_ns.class_(
|
DateTimeStateTrigger = datetime_ns.class_(
|
||||||
"DateTimeStateTrigger", automation.Trigger.template(cg.ESPTime)
|
"DateTimeStateTrigger", automation.Trigger.template(cg.ESPTime)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
OnTimeTrigger = datetime_ns.class_(
|
||||||
|
"OnTimeTrigger", automation.Trigger, cg.Component, cg.Parented.template(TimeEntity)
|
||||||
|
)
|
||||||
|
|
||||||
DATETIME_MODES = [
|
DATETIME_MODES = [
|
||||||
"DATE",
|
"DATE",
|
||||||
"TIME",
|
"TIME",
|
||||||
|
@ -44,7 +56,6 @@ DATETIME_MODES = [
|
||||||
|
|
||||||
_DATETIME_SCHEMA = cv.Schema(
|
_DATETIME_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTDatetimeComponent),
|
|
||||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger),
|
||||||
|
@ -57,6 +68,7 @@ _DATETIME_SCHEMA = cv.Schema(
|
||||||
def date_schema(class_: MockObjClass) -> cv.Schema:
|
def date_schema(class_: MockObjClass) -> cv.Schema:
|
||||||
schema = {
|
schema = {
|
||||||
cv.GenerateID(): cv.declare_id(class_),
|
cv.GenerateID(): cv.declare_id(class_),
|
||||||
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTDateComponent),
|
||||||
cv.Optional(CONF_TYPE, default="DATE"): cv.one_of("DATE", upper=True),
|
cv.Optional(CONF_TYPE, default="DATE"): cv.one_of("DATE", upper=True),
|
||||||
}
|
}
|
||||||
return _DATETIME_SCHEMA.extend(schema)
|
return _DATETIME_SCHEMA.extend(schema)
|
||||||
|
@ -65,7 +77,20 @@ def date_schema(class_: MockObjClass) -> cv.Schema:
|
||||||
def time_schema(class_: MockObjClass) -> cv.Schema:
|
def time_schema(class_: MockObjClass) -> cv.Schema:
|
||||||
schema = {
|
schema = {
|
||||||
cv.GenerateID(): cv.declare_id(class_),
|
cv.GenerateID(): cv.declare_id(class_),
|
||||||
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTimeComponent),
|
||||||
cv.Optional(CONF_TYPE, default="TIME"): cv.one_of("TIME", upper=True),
|
cv.Optional(CONF_TYPE, default="TIME"): cv.one_of("TIME", upper=True),
|
||||||
|
cv.Inclusive(
|
||||||
|
CONF_ON_TIME,
|
||||||
|
group_of_inclusion=CONF_ON_TIME,
|
||||||
|
msg="`on_time` and `time_id` must both be specified",
|
||||||
|
): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OnTimeTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Inclusive(CONF_TIME_ID, group_of_inclusion=CONF_ON_TIME): cv.use_id(
|
||||||
|
time.RealTimeClock
|
||||||
|
),
|
||||||
}
|
}
|
||||||
return _DATETIME_SCHEMA.extend(schema)
|
return _DATETIME_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
@ -88,6 +113,17 @@ async def setup_datetime_core_(var, config):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf)
|
await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf)
|
||||||
|
|
||||||
|
rtc_id = config.get(CONF_TIME_ID)
|
||||||
|
rtc = None
|
||||||
|
if rtc_id is not None:
|
||||||
|
rtc = await cg.get_variable(rtc_id)
|
||||||
|
for conf in config.get(CONF_ON_TIME, []):
|
||||||
|
assert rtc is not None
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], rtc)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
await cg.register_component(trigger, conf)
|
||||||
|
await cg.register_parented(trigger, var)
|
||||||
|
|
||||||
|
|
||||||
async def register_datetime(var, config):
|
async def register_datetime(var, config):
|
||||||
if not CORE.has_id(config[CONF_ID]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
@ -109,18 +145,12 @@ async def to_code(config):
|
||||||
cg.add_global(datetime_ns.using)
|
cg.add_global(datetime_ns.using)
|
||||||
|
|
||||||
|
|
||||||
OPERATION_BASE_SCHEMA = cv.Schema(
|
|
||||||
{
|
|
||||||
cv.Required(CONF_ID): cv.use_id(DateEntity),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"datetime.date.set",
|
"datetime.date.set",
|
||||||
DateSetAction,
|
DateSetAction,
|
||||||
OPERATION_BASE_SCHEMA.extend(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(DateEntity),
|
||||||
cv.Required(CONF_DATE): cv.Any(
|
cv.Required(CONF_DATE): cv.Any(
|
||||||
cv.returning_lambda, cv.date_time(allowed_time=False)
|
cv.returning_lambda, cv.date_time(allowed_time=False)
|
||||||
),
|
),
|
||||||
|
@ -144,3 +174,34 @@ async def datetime_date_set_to_code(config, action_id, template_arg, args):
|
||||||
)
|
)
|
||||||
cg.add(action_var.set_date(date_struct))
|
cg.add(action_var.set_date(date_struct))
|
||||||
return action_var
|
return action_var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"datetime.time.set",
|
||||||
|
TimeSetAction,
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(TimeEntity),
|
||||||
|
cv.Required(CONF_TIME): cv.Any(
|
||||||
|
cv.returning_lambda, cv.date_time(allowed_date=False)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def datetime_time_set_to_code(config, action_id, template_arg, args):
|
||||||
|
action_var = cg.new_Pvariable(action_id, template_arg)
|
||||||
|
await cg.register_parented(action_var, config[CONF_ID])
|
||||||
|
|
||||||
|
time_config = config[CONF_TIME]
|
||||||
|
if cg.is_template(time_config):
|
||||||
|
template_ = await cg.templatable(config[CONF_TIME], [], cg.ESPTime)
|
||||||
|
cg.add(action_var.set_time(template_))
|
||||||
|
else:
|
||||||
|
time_struct = cg.StructInitializer(
|
||||||
|
cg.ESPTime,
|
||||||
|
("second", time_config[CONF_SECOND]),
|
||||||
|
("minute", time_config[CONF_MINUTE]),
|
||||||
|
("hour", time_config[CONF_HOUR]),
|
||||||
|
)
|
||||||
|
cg.add(action_var.set_time(time_struct))
|
||||||
|
return action_var
|
||||||
|
|
156
esphome/components/datetime/time_entity.cpp
Normal file
156
esphome/components/datetime/time_entity.cpp
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
#include "time_entity.h"
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace datetime {
|
||||||
|
|
||||||
|
static const char *const TAG = "datetime.time_entity";
|
||||||
|
|
||||||
|
void TimeEntity::publish_state() {
|
||||||
|
if (this->hour_ > 23) {
|
||||||
|
this->has_state_ = false;
|
||||||
|
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->minute_ > 59) {
|
||||||
|
this->has_state_ = false;
|
||||||
|
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->second_ > 59) {
|
||||||
|
this->has_state_ = false;
|
||||||
|
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->has_state_ = true;
|
||||||
|
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
|
||||||
|
this->second_);
|
||||||
|
this->state_callback_.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeCall TimeEntity::make_call() { return TimeCall(this); }
|
||||||
|
|
||||||
|
void TimeCall::validate_() {
|
||||||
|
if (this->hour_.has_value() && this->hour_ > 23) {
|
||||||
|
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
||||||
|
this->hour_.reset();
|
||||||
|
}
|
||||||
|
if (this->minute_.has_value() && this->minute_ > 59) {
|
||||||
|
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
||||||
|
this->minute_.reset();
|
||||||
|
}
|
||||||
|
if (this->second_.has_value() && this->second_ > 59) {
|
||||||
|
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
||||||
|
this->second_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeCall::perform() {
|
||||||
|
this->validate_();
|
||||||
|
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
|
||||||
|
if (this->hour_.has_value()) {
|
||||||
|
ESP_LOGD(TAG, " Hour: %d", *this->hour_);
|
||||||
|
}
|
||||||
|
if (this->minute_.has_value()) {
|
||||||
|
ESP_LOGD(TAG, " Minute: %d", *this->minute_);
|
||||||
|
}
|
||||||
|
if (this->second_.has_value()) {
|
||||||
|
ESP_LOGD(TAG, " Second: %d", *this->second_);
|
||||||
|
}
|
||||||
|
this->parent_->control(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeCall &TimeCall::set_time(uint8_t hour, uint8_t minute, uint8_t second) {
|
||||||
|
this->hour_ = hour;
|
||||||
|
this->minute_ = minute;
|
||||||
|
this->second_ = second;
|
||||||
|
return *this;
|
||||||
|
};
|
||||||
|
|
||||||
|
TimeCall &TimeCall::set_time(ESPTime time) { return this->set_time(time.hour, time.minute, time.second); };
|
||||||
|
|
||||||
|
TimeCall &TimeCall::set_time(const std::string &time) {
|
||||||
|
ESPTime val{};
|
||||||
|
if (!ESPTime::strptime(time, val)) {
|
||||||
|
ESP_LOGE(TAG, "Could not convert the time string to an ESPTime object");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
return this->set_time(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeCall TimeEntityRestoreState::to_call(TimeEntity *time) {
|
||||||
|
TimeCall call = time->make_call();
|
||||||
|
call.set_time(this->hour, this->minute, this->second);
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeEntityRestoreState::apply(TimeEntity *time) {
|
||||||
|
time->hour_ = this->hour;
|
||||||
|
time->minute_ = this->minute;
|
||||||
|
time->second_ = this->second;
|
||||||
|
time->publish_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
|
|
||||||
|
static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider
|
||||||
|
// there has been a drastic time synchronization
|
||||||
|
|
||||||
|
void OnTimeTrigger::loop() {
|
||||||
|
if (!this->parent_->has_state()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESPTime time = this->rtc_->now();
|
||||||
|
if (!time.is_valid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->last_check_.has_value()) {
|
||||||
|
if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) {
|
||||||
|
// We went back in time (a lot), probably caused by time synchronization
|
||||||
|
ESP_LOGW(TAG, "Time has jumped back!");
|
||||||
|
} else if (*this->last_check_ >= time) {
|
||||||
|
// already handled this one
|
||||||
|
return;
|
||||||
|
} else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) {
|
||||||
|
// We went ahead in time (a lot), probably caused by time synchronization
|
||||||
|
ESP_LOGW(TAG, "Time has jumped ahead!");
|
||||||
|
this->last_check_ = time;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
this->last_check_->increment_second();
|
||||||
|
if (*this->last_check_ >= time)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (this->matches_(*this->last_check_)) {
|
||||||
|
this->trigger();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->last_check_ = time;
|
||||||
|
if (!time.fields_in_range()) {
|
||||||
|
ESP_LOGW(TAG, "Time is out of range!");
|
||||||
|
ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u", time.second, time.minute, time.hour);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->matches_(time))
|
||||||
|
this->trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OnTimeTrigger::matches_(const ESPTime &time) const {
|
||||||
|
return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute &&
|
||||||
|
time.second == this->parent_->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace datetime
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_DATETIME_TIME
|
137
esphome/components/datetime/time_entity.h
Normal file
137
esphome/components/datetime/time_entity.h
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/time.h"
|
||||||
|
|
||||||
|
#include "datetime_base.h"
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
|
#include "esphome/components/time/real_time_clock.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace datetime {
|
||||||
|
|
||||||
|
#define LOG_DATETIME_TIME(prefix, type, obj) \
|
||||||
|
if ((obj) != nullptr) { \
|
||||||
|
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||||
|
if (!(obj)->get_icon().empty()) { \
|
||||||
|
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
class TimeCall;
|
||||||
|
class TimeEntity;
|
||||||
|
|
||||||
|
struct TimeEntityRestoreState {
|
||||||
|
uint8_t hour;
|
||||||
|
uint8_t minute;
|
||||||
|
uint8_t second;
|
||||||
|
|
||||||
|
TimeCall to_call(TimeEntity *time);
|
||||||
|
void apply(TimeEntity *time);
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
class TimeEntity : public DateTimeBase {
|
||||||
|
protected:
|
||||||
|
uint8_t hour_;
|
||||||
|
uint8_t minute_;
|
||||||
|
uint8_t second_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void publish_state();
|
||||||
|
TimeCall make_call();
|
||||||
|
|
||||||
|
ESPTime state_as_esptime() const override {
|
||||||
|
ESPTime obj;
|
||||||
|
obj.hour = this->hour_;
|
||||||
|
obj.minute = this->minute_;
|
||||||
|
obj.second = this->second_;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t &hour = hour_;
|
||||||
|
const uint8_t &minute = minute_;
|
||||||
|
const uint8_t &second = second_;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class TimeCall;
|
||||||
|
friend struct TimeEntityRestoreState;
|
||||||
|
|
||||||
|
virtual void control(const TimeCall &call) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TimeCall {
|
||||||
|
public:
|
||||||
|
explicit TimeCall(TimeEntity *parent) : parent_(parent) {}
|
||||||
|
void perform();
|
||||||
|
TimeCall &set_time(uint8_t hour, uint8_t minute, uint8_t second);
|
||||||
|
TimeCall &set_time(ESPTime time);
|
||||||
|
TimeCall &set_time(const std::string &time);
|
||||||
|
|
||||||
|
TimeCall &set_hour(uint8_t hour) {
|
||||||
|
this->hour_ = hour;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
TimeCall &set_minute(uint8_t minute) {
|
||||||
|
this->minute_ = minute;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
TimeCall &set_second(uint8_t second) {
|
||||||
|
this->second_ = second;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<uint8_t> get_hour() const { return this->hour_; }
|
||||||
|
optional<uint8_t> get_minute() const { return this->minute_; }
|
||||||
|
optional<uint8_t> get_second() const { return this->second_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void validate_();
|
||||||
|
|
||||||
|
TimeEntity *parent_;
|
||||||
|
|
||||||
|
optional<uint8_t> hour_;
|
||||||
|
optional<uint8_t> minute_;
|
||||||
|
optional<uint8_t> second_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class TimeSetAction : public Action<Ts...>, public Parented<TimeEntity> {
|
||||||
|
public:
|
||||||
|
TEMPLATABLE_VALUE(ESPTime, time)
|
||||||
|
|
||||||
|
void play(Ts... x) override {
|
||||||
|
auto call = this->parent_->make_call();
|
||||||
|
|
||||||
|
if (this->time_.has_value()) {
|
||||||
|
call.set_time(this->time_.value(x...));
|
||||||
|
}
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
|
|
||||||
|
class OnTimeTrigger : public Trigger<>, public Component, public Parented<TimeEntity> {
|
||||||
|
public:
|
||||||
|
explicit OnTimeTrigger(time::RealTimeClock *rtc) : rtc_(rtc) {}
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool matches_(const ESPTime &time) const;
|
||||||
|
|
||||||
|
time::RealTimeClock *rtc_;
|
||||||
|
optional<ESPTime> last_check_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace datetime
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_DATETIME_TIME
|
|
@ -60,6 +60,8 @@ void DisplayMenuComponent::left() {
|
||||||
if (this->editing_) {
|
if (this->editing_) {
|
||||||
this->finish_editing_();
|
this->finish_editing_();
|
||||||
changed = true;
|
changed = true;
|
||||||
|
} else {
|
||||||
|
changed = this->leave_menu_();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MENU_MODE_JOYSTICK:
|
case MENU_MODE_JOYSTICK:
|
||||||
|
|
55
esphome/components/esp32_rmt/__init__.py
Normal file
55
esphome/components/esp32_rmt/__init__.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
import esphome.codegen as cg
|
||||||
|
|
||||||
|
from esphome.components import esp32
|
||||||
|
|
||||||
|
CODEOWNERS = ["@jesserockz"]
|
||||||
|
|
||||||
|
RMT_TX_CHANNELS = {
|
||||||
|
esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
|
||||||
|
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
|
||||||
|
esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3],
|
||||||
|
esp32.const.VARIANT_ESP32C3: [0, 1],
|
||||||
|
esp32.const.VARIANT_ESP32C6: [0, 1],
|
||||||
|
esp32.const.VARIANT_ESP32H2: [0, 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
RMT_RX_CHANNELS = {
|
||||||
|
esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
|
||||||
|
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
|
||||||
|
esp32.const.VARIANT_ESP32S3: [4, 5, 6, 7],
|
||||||
|
esp32.const.VARIANT_ESP32C3: [2, 3],
|
||||||
|
esp32.const.VARIANT_ESP32C6: [2, 3],
|
||||||
|
esp32.const.VARIANT_ESP32H2: [2, 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
rmt_channel_t = cg.global_ns.enum("rmt_channel_t")
|
||||||
|
RMT_CHANNEL_ENUMS = {
|
||||||
|
0: rmt_channel_t.RMT_CHANNEL_0,
|
||||||
|
1: rmt_channel_t.RMT_CHANNEL_1,
|
||||||
|
2: rmt_channel_t.RMT_CHANNEL_2,
|
||||||
|
3: rmt_channel_t.RMT_CHANNEL_3,
|
||||||
|
4: rmt_channel_t.RMT_CHANNEL_4,
|
||||||
|
5: rmt_channel_t.RMT_CHANNEL_5,
|
||||||
|
6: rmt_channel_t.RMT_CHANNEL_6,
|
||||||
|
7: rmt_channel_t.RMT_CHANNEL_7,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_rmt_channel(*, tx: bool):
|
||||||
|
|
||||||
|
rmt_channels = RMT_TX_CHANNELS if tx else RMT_RX_CHANNELS
|
||||||
|
|
||||||
|
def _validator(value):
|
||||||
|
cv.only_on_esp32(value)
|
||||||
|
value = cv.int_(value)
|
||||||
|
variant = esp32.get_esp32_variant()
|
||||||
|
if variant not in rmt_channels:
|
||||||
|
raise cv.Invalid(f"ESP32 variant {variant} does not support RMT.")
|
||||||
|
if value not in rmt_channels[variant]:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"RMT channel {value} does not support {'transmitting' if tx else 'receiving'} for ESP32 variant {variant}."
|
||||||
|
)
|
||||||
|
return cv.enum(RMT_CHANNEL_ENUMS)(value)
|
||||||
|
|
||||||
|
return _validator
|
|
@ -3,7 +3,7 @@ from dataclasses import dataclass
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.components import esp32, light
|
from esphome.components import esp32_rmt, light
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_CHIPSET,
|
CONF_CHIPSET,
|
||||||
CONF_MAX_REFRESH_RATE,
|
CONF_MAX_REFRESH_RATE,
|
||||||
|
@ -11,6 +11,7 @@ from esphome.const import (
|
||||||
CONF_OUTPUT_ID,
|
CONF_OUTPUT_ID,
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
CONF_RGB_ORDER,
|
CONF_RGB_ORDER,
|
||||||
|
CONF_RMT_CHANNEL,
|
||||||
)
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@jesserockz"]
|
CODEOWNERS = ["@jesserockz"]
|
||||||
|
@ -57,27 +58,6 @@ CONF_BIT0_HIGH = "bit0_high"
|
||||||
CONF_BIT0_LOW = "bit0_low"
|
CONF_BIT0_LOW = "bit0_low"
|
||||||
CONF_BIT1_HIGH = "bit1_high"
|
CONF_BIT1_HIGH = "bit1_high"
|
||||||
CONF_BIT1_LOW = "bit1_low"
|
CONF_BIT1_LOW = "bit1_low"
|
||||||
CONF_RMT_CHANNEL = "rmt_channel"
|
|
||||||
|
|
||||||
RMT_CHANNELS = {
|
|
||||||
esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
|
|
||||||
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
|
|
||||||
esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3],
|
|
||||||
esp32.const.VARIANT_ESP32C3: [0, 1],
|
|
||||||
esp32.const.VARIANT_ESP32C6: [0, 1],
|
|
||||||
esp32.const.VARIANT_ESP32H2: [0, 1],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_rmt_channel(value):
|
|
||||||
variant = esp32.get_esp32_variant()
|
|
||||||
if variant not in RMT_CHANNELS:
|
|
||||||
raise cv.Invalid(f"ESP32 variant {variant} does not support RMT.")
|
|
||||||
if value not in RMT_CHANNELS[variant]:
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"RMT channel {value} is not supported for ESP32 variant {variant}."
|
|
||||||
)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
@ -87,7 +67,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
|
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
|
||||||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||||
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
|
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
|
||||||
cv.Required(CONF_RMT_CHANNEL): _validate_rmt_channel,
|
cv.Required(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True),
|
||||||
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
||||||
cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
|
cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
|
||||||
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
|
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import esphome.final_validate as fv
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_FREQUENCY,
|
CONF_FREQUENCY,
|
||||||
|
CONF_TIMEOUT,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_INPUT,
|
CONF_INPUT,
|
||||||
CONF_OUTPUT,
|
CONF_OUTPUT,
|
||||||
|
@ -59,6 +60,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All(
|
cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All(
|
||||||
cv.frequency, cv.Range(min=0, min_included=False)
|
cv.frequency, cv.Range(min=0, min_included=False)
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_TIMEOUT): cv.positive_time_period,
|
||||||
cv.Optional(CONF_SCAN, default=True): cv.boolean,
|
cv.Optional(CONF_SCAN, default=True): cv.boolean,
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
|
@ -81,6 +83,8 @@ async def to_code(config):
|
||||||
|
|
||||||
cg.add(var.set_frequency(int(config[CONF_FREQUENCY])))
|
cg.add(var.set_frequency(int(config[CONF_FREQUENCY])))
|
||||||
cg.add(var.set_scan(config[CONF_SCAN]))
|
cg.add(var.set_scan(config[CONF_SCAN]))
|
||||||
|
if CONF_TIMEOUT in config:
|
||||||
|
cg.add(var.set_timeout(int(config[CONF_TIMEOUT].total_microseconds)))
|
||||||
if CORE.using_arduino:
|
if CORE.using_arduino:
|
||||||
cg.add_library("Wire", None)
|
cg.add_library("Wire", None)
|
||||||
|
|
||||||
|
@ -119,23 +123,56 @@ async def register_i2c_device(var, config):
|
||||||
|
|
||||||
|
|
||||||
def final_validate_device_schema(
|
def final_validate_device_schema(
|
||||||
name: str, *, min_frequency: cv.frequency = None, max_frequency: cv.frequency = None
|
name: str,
|
||||||
|
*,
|
||||||
|
min_frequency: cv.frequency = None,
|
||||||
|
max_frequency: cv.frequency = None,
|
||||||
|
min_timeout: cv.time_period = None,
|
||||||
|
max_timeout: cv.time_period = None,
|
||||||
):
|
):
|
||||||
hub_schema = {}
|
hub_schema = {}
|
||||||
if min_frequency is not None:
|
if (min_frequency is not None) and (max_frequency is not None):
|
||||||
|
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
|
||||||
|
min=cv.frequency(min_frequency),
|
||||||
|
min_included=True,
|
||||||
|
max=cv.frequency(max_frequency),
|
||||||
|
max_included=True,
|
||||||
|
msg=f"Component {name} requires a frequency between {min_frequency} and {max_frequency} for the I2C bus",
|
||||||
|
)
|
||||||
|
elif min_frequency is not None:
|
||||||
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
|
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
|
||||||
min=cv.frequency(min_frequency),
|
min=cv.frequency(min_frequency),
|
||||||
min_included=True,
|
min_included=True,
|
||||||
msg=f"Component {name} requires a minimum frequency of {min_frequency} for the I2C bus",
|
msg=f"Component {name} requires a minimum frequency of {min_frequency} for the I2C bus",
|
||||||
)
|
)
|
||||||
|
elif max_frequency is not None:
|
||||||
if max_frequency is not None:
|
|
||||||
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
|
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
|
||||||
max=cv.frequency(max_frequency),
|
max=cv.frequency(max_frequency),
|
||||||
max_included=True,
|
max_included=True,
|
||||||
msg=f"Component {name} cannot be used with a frequency of over {max_frequency} for the I2C bus",
|
msg=f"Component {name} cannot be used with a frequency of over {max_frequency} for the I2C bus",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (min_timeout is not None) and (max_timeout is not None):
|
||||||
|
hub_schema[cv.Required(CONF_TIMEOUT)] = cv.Range(
|
||||||
|
min=cv.time_period(min_timeout),
|
||||||
|
min_included=True,
|
||||||
|
max=cv.time_period(max_timeout),
|
||||||
|
max_included=True,
|
||||||
|
msg=f"Component {name} requires a timeout between {min_timeout} and {max_timeout} for the I2C bus",
|
||||||
|
)
|
||||||
|
elif min_timeout is not None:
|
||||||
|
hub_schema[cv.Required(CONF_TIMEOUT)] = cv.Range(
|
||||||
|
min=cv.time_period(min_timeout),
|
||||||
|
min_included=True,
|
||||||
|
msg=f"Component {name} requires a minimum timeout of {min_timeout} for the I2C bus",
|
||||||
|
)
|
||||||
|
elif max_timeout is not None:
|
||||||
|
hub_schema[cv.Required(CONF_TIMEOUT)] = cv.Range(
|
||||||
|
max=cv.time_period(max_timeout),
|
||||||
|
max_included=True,
|
||||||
|
msg=f"Component {name} cannot be used with a timeout of over {max_timeout} for the I2C bus",
|
||||||
|
)
|
||||||
|
|
||||||
return cv.Schema(
|
return cv.Schema(
|
||||||
{cv.Required(CONF_I2C_ID): fv.id_declaration_match_schema(hub_schema)},
|
{cv.Required(CONF_I2C_ID): fv.id_declaration_match_schema(hub_schema)},
|
||||||
extra=cv.ALLOW_EXTRA,
|
extra=cv.ALLOW_EXTRA,
|
||||||
|
|
|
@ -52,6 +52,18 @@ void ArduinoI2CBus::set_pins_and_clock_() {
|
||||||
#else
|
#else
|
||||||
wire_->begin(static_cast<int>(sda_pin_), static_cast<int>(scl_pin_));
|
wire_->begin(static_cast<int>(sda_pin_), static_cast<int>(scl_pin_));
|
||||||
#endif
|
#endif
|
||||||
|
if (timeout_ > 0) { // if timeout specified in yaml
|
||||||
|
#if defined(USE_ESP32)
|
||||||
|
// https://github.com/espressif/arduino-esp32/blob/master/libraries/Wire/src/Wire.cpp
|
||||||
|
wire_->setTimeOut(timeout_ / 1000); // unit: ms
|
||||||
|
#elif defined(USE_ESP8266)
|
||||||
|
// https://github.com/esp8266/Arduino/blob/master/libraries/Wire/Wire.h
|
||||||
|
wire_->setClockStretchLimit(timeout_); // unit: us
|
||||||
|
#elif defined(USE_RP2040)
|
||||||
|
// https://github.com/earlephilhower/ArduinoCore-API/blob/e37df85425e0ac020bfad226d927f9b00d2e0fb7/api/Stream.h
|
||||||
|
wire_->setTimeout(timeout_ / 1000); // unit: ms
|
||||||
|
#endif
|
||||||
|
}
|
||||||
wire_->setClock(frequency_);
|
wire_->setClock(frequency_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +72,15 @@ void ArduinoI2CBus::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_);
|
ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_);
|
ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_);
|
ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_);
|
||||||
|
if (timeout_ > 0) {
|
||||||
|
#if defined(USE_ESP32)
|
||||||
|
ESP_LOGCONFIG(TAG, " Timeout: %u ms", this->timeout_ / 1000);
|
||||||
|
#elif defined(USE_ESP8266)
|
||||||
|
ESP_LOGCONFIG(TAG, " Timeout: %u us", this->timeout_);
|
||||||
|
#elif defined(USE_RP2040)
|
||||||
|
ESP_LOGCONFIG(TAG, " Timeout: %u ms", this->timeout_ / 1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
switch (this->recovery_result_) {
|
switch (this->recovery_result_) {
|
||||||
case RECOVERY_COMPLETED:
|
case RECOVERY_COMPLETED:
|
||||||
ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
|
ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
|
||||||
|
|
|
@ -27,6 +27,7 @@ class ArduinoI2CBus : public I2CBus, public Component {
|
||||||
void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; }
|
void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; }
|
||||||
void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; }
|
void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; }
|
||||||
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
||||||
|
void set_timeout(uint32_t timeout) { timeout_ = timeout; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void recover_();
|
void recover_();
|
||||||
|
@ -38,6 +39,7 @@ class ArduinoI2CBus : public I2CBus, public Component {
|
||||||
uint8_t sda_pin_;
|
uint8_t sda_pin_;
|
||||||
uint8_t scl_pin_;
|
uint8_t scl_pin_;
|
||||||
uint32_t frequency_;
|
uint32_t frequency_;
|
||||||
|
uint32_t timeout_ = 0;
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#ifdef USE_ESP_IDF
|
#ifdef USE_ESP_IDF
|
||||||
|
|
||||||
#include "i2c_bus_esp_idf.h"
|
#include "i2c_bus_esp_idf.h"
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/application.h"
|
|
||||||
#include <cstring>
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <cstring>
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace i2c {
|
namespace i2c {
|
||||||
|
@ -45,6 +45,20 @@ void IDFI2CBus::setup() {
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (timeout_ > 0) { // if timeout specified in yaml:
|
||||||
|
if (timeout_ > 13000) {
|
||||||
|
ESP_LOGW(TAG, "i2c timeout of %" PRIu32 "us greater than max of 13ms on esp-idf, setting to max", timeout_);
|
||||||
|
timeout_ = 13000;
|
||||||
|
}
|
||||||
|
err = i2c_set_timeout(port_, timeout_ * 80); // unit: APB 80MHz clock cycle
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "i2c_set_timeout failed: %s", esp_err_to_name(err));
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
ESP_LOGV(TAG, "i2c_timeout set to %d ticks (%d us)", timeout_ * 80, timeout_);
|
||||||
|
}
|
||||||
|
}
|
||||||
err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, ESP_INTR_FLAG_IRAM);
|
err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, ESP_INTR_FLAG_IRAM);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "i2c_driver_install failed: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "i2c_driver_install failed: %s", esp_err_to_name(err));
|
||||||
|
@ -62,6 +76,9 @@ void IDFI2CBus::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_);
|
ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_);
|
ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " Frequency: %" PRIu32 " Hz", this->frequency_);
|
ESP_LOGCONFIG(TAG, " Frequency: %" PRIu32 " Hz", this->frequency_);
|
||||||
|
if (timeout_ > 0) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 "us", this->timeout_);
|
||||||
|
}
|
||||||
switch (this->recovery_result_) {
|
switch (this->recovery_result_) {
|
||||||
case RECOVERY_COMPLETED:
|
case RECOVERY_COMPLETED:
|
||||||
ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
|
ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
|
||||||
|
@ -127,6 +144,8 @@ ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
|
||||||
return ERROR_UNKNOWN;
|
return ERROR_UNKNOWN;
|
||||||
}
|
}
|
||||||
err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS);
|
err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS);
|
||||||
|
// i2c_master_cmd_begin() will block for a whole second if no ack:
|
||||||
|
// https://github.com/espressif/esp-idf/issues/4999
|
||||||
i2c_cmd_link_delete(cmd);
|
i2c_cmd_link_delete(cmd);
|
||||||
if (err == ESP_FAIL) {
|
if (err == ESP_FAIL) {
|
||||||
// transfer not acked
|
// transfer not acked
|
||||||
|
|
|
@ -29,6 +29,7 @@ class IDFI2CBus : public I2CBus, public Component {
|
||||||
void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; }
|
void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; }
|
||||||
void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; }
|
void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; }
|
||||||
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
||||||
|
void set_timeout(uint32_t timeout) { timeout_ = timeout; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void recover_();
|
void recover_();
|
||||||
|
@ -41,6 +42,7 @@ class IDFI2CBus : public I2CBus, public Component {
|
||||||
uint8_t scl_pin_;
|
uint8_t scl_pin_;
|
||||||
bool scl_pullup_enabled_;
|
bool scl_pullup_enabled_;
|
||||||
uint32_t frequency_;
|
uint32_t frequency_;
|
||||||
|
uint32_t timeout_ = 0;
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,12 @@ static const char *const TAG = "audio";
|
||||||
void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
|
void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
|
||||||
if (call.get_media_url().has_value()) {
|
if (call.get_media_url().has_value()) {
|
||||||
this->current_url_ = call.get_media_url();
|
this->current_url_ = call.get_media_url();
|
||||||
|
if (this->i2s_state_ != I2S_STATE_STOPPED && this->audio_ != nullptr) {
|
||||||
if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && this->audio_ != nullptr) {
|
|
||||||
if (this->audio_->isRunning()) {
|
if (this->audio_->isRunning()) {
|
||||||
this->audio_->stopSong();
|
this->audio_->stopSong();
|
||||||
}
|
}
|
||||||
this->audio_->connecttohost(this->current_url_.value().c_str());
|
this->audio_->connecttohost(this->current_url_.value().c_str());
|
||||||
|
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
|
||||||
} else {
|
} else {
|
||||||
this->start();
|
this->start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,11 @@ uint8_t temprature_sens_read();
|
||||||
#ifdef USE_RP2040
|
#ifdef USE_RP2040
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#endif // USE_RP2040
|
#endif // USE_RP2040
|
||||||
|
#ifdef USE_BK72XX
|
||||||
|
extern "C" {
|
||||||
|
uint32_t temp_single_get_current_temperature(uint32_t *temp_value);
|
||||||
|
}
|
||||||
|
#endif // USE_BK72XX
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace internal_temperature {
|
namespace internal_temperature {
|
||||||
|
@ -46,6 +51,16 @@ void InternalTemperatureSensor::update() {
|
||||||
temperature = analogReadTemp();
|
temperature = analogReadTemp();
|
||||||
success = (temperature != 0.0f);
|
success = (temperature != 0.0f);
|
||||||
#endif // USE_RP2040
|
#endif // USE_RP2040
|
||||||
|
#ifdef USE_BK72XX
|
||||||
|
uint32_t raw, result;
|
||||||
|
result = temp_single_get_current_temperature(&raw);
|
||||||
|
success = (result == 0);
|
||||||
|
#ifdef USE_LIBRETINY_VARIANT_BK7231T
|
||||||
|
temperature = raw * 0.04f;
|
||||||
|
#else
|
||||||
|
temperature = raw * 0.128f;
|
||||||
|
#endif // USE_LIBRETINY_VARIANT_BK7231T
|
||||||
|
#endif // USE_BK72XX
|
||||||
if (success && std::isfinite(temperature)) {
|
if (success && std::isfinite(temperature)) {
|
||||||
this->publish_state(temperature);
|
this->publish_state(temperature);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,6 +14,7 @@ from esphome.const import (
|
||||||
KEY_FRAMEWORK_VERSION,
|
KEY_FRAMEWORK_VERSION,
|
||||||
PLATFORM_ESP32,
|
PLATFORM_ESP32,
|
||||||
PLATFORM_RP2040,
|
PLATFORM_RP2040,
|
||||||
|
PLATFORM_BK72XX,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
).extend(cv.polling_component_schema("60s")),
|
).extend(cv.polling_component_schema("60s")),
|
||||||
cv.only_on([PLATFORM_ESP32, PLATFORM_RP2040]),
|
cv.only_on([PLATFORM_ESP32, PLATFORM_RP2040, PLATFORM_BK72XX]),
|
||||||
validate_config,
|
validate_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ def _notify_old_style(config):
|
||||||
ARDUINO_VERSIONS = {
|
ARDUINO_VERSIONS = {
|
||||||
"dev": (cv.Version(0, 0, 0), "https://github.com/libretiny-eu/libretiny.git"),
|
"dev": (cv.Version(0, 0, 0), "https://github.com/libretiny-eu/libretiny.git"),
|
||||||
"latest": (cv.Version(0, 0, 0), None),
|
"latest": (cv.Version(0, 0, 0), None),
|
||||||
"recommended": (cv.Version(1, 4, 1), None),
|
"recommended": (cv.Version(1, 5, 1), None),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ class DataTrigger : public Trigger<const std::vector<int16_t> &> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class IsCapturingActon : public Condition<Ts...>, public Parented<Microphone> {
|
template<typename... Ts> class IsCapturingCondition : public Condition<Ts...>, public Parented<Microphone> {
|
||||||
public:
|
public:
|
||||||
bool check(Ts... x) override { return this->parent_->is_running(); }
|
bool check(Ts... x) override { return this->parent_->is_running(); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -113,7 +113,8 @@ MQTTSensorComponent = mqtt_ns.class_("MQTTSensorComponent", MQTTComponent)
|
||||||
MQTTSwitchComponent = mqtt_ns.class_("MQTTSwitchComponent", MQTTComponent)
|
MQTTSwitchComponent = mqtt_ns.class_("MQTTSwitchComponent", MQTTComponent)
|
||||||
MQTTTextSensor = mqtt_ns.class_("MQTTTextSensor", MQTTComponent)
|
MQTTTextSensor = mqtt_ns.class_("MQTTTextSensor", MQTTComponent)
|
||||||
MQTTNumberComponent = mqtt_ns.class_("MQTTNumberComponent", MQTTComponent)
|
MQTTNumberComponent = mqtt_ns.class_("MQTTNumberComponent", MQTTComponent)
|
||||||
MQTTDatetimeComponent = mqtt_ns.class_("MQTTDatetimeComponent", MQTTComponent)
|
MQTTDateComponent = mqtt_ns.class_("MQTTDateComponent", MQTTComponent)
|
||||||
|
MQTTTimeComponent = mqtt_ns.class_("MQTTTimeComponent", MQTTComponent)
|
||||||
MQTTTextComponent = mqtt_ns.class_("MQTTTextComponent", MQTTComponent)
|
MQTTTextComponent = mqtt_ns.class_("MQTTTextComponent", MQTTComponent)
|
||||||
MQTTSelectComponent = mqtt_ns.class_("MQTTSelectComponent", MQTTComponent)
|
MQTTSelectComponent = mqtt_ns.class_("MQTTSelectComponent", MQTTComponent)
|
||||||
MQTTButtonComponent = mqtt_ns.class_("MQTTButtonComponent", MQTTComponent)
|
MQTTButtonComponent = mqtt_ns.class_("MQTTButtonComponent", MQTTComponent)
|
||||||
|
|
|
@ -13,9 +13,9 @@ namespace mqtt {
|
||||||
|
|
||||||
class MQTTDateComponent : public mqtt::MQTTComponent {
|
class MQTTDateComponent : public mqtt::MQTTComponent {
|
||||||
public:
|
public:
|
||||||
/** Construct this MQTTDatetimeComponent instance with the provided friendly_name and datetime
|
/** Construct this MQTTDateComponent instance with the provided friendly_name and date
|
||||||
*
|
*
|
||||||
* @param datetime The datetime component.
|
* @param date The date component.
|
||||||
*/
|
*/
|
||||||
explicit MQTTDateComponent(datetime::DateEntity *date);
|
explicit MQTTDateComponent(datetime::DateEntity *date);
|
||||||
|
|
||||||
|
|
68
esphome/components/mqtt/mqtt_time.cpp
Normal file
68
esphome/components/mqtt/mqtt_time.cpp
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#include "mqtt_time.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include "mqtt_const.h"
|
||||||
|
|
||||||
|
#ifdef USE_MQTT
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace mqtt {
|
||||||
|
|
||||||
|
static const char *const TAG = "mqtt.datetime.time";
|
||||||
|
|
||||||
|
using namespace esphome::datetime;
|
||||||
|
|
||||||
|
MQTTTimeComponent::MQTTTimeComponent(TimeEntity *time) : time_(time) {}
|
||||||
|
|
||||||
|
void MQTTTimeComponent::setup() {
|
||||||
|
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
|
||||||
|
auto call = this->time_->make_call();
|
||||||
|
if (root.containsKey("hour")) {
|
||||||
|
call.set_hour(root["hour"]);
|
||||||
|
}
|
||||||
|
if (root.containsKey("minute")) {
|
||||||
|
call.set_minute(root["minute"]);
|
||||||
|
}
|
||||||
|
if (root.containsKey("second")) {
|
||||||
|
call.set_second(root["second"]);
|
||||||
|
}
|
||||||
|
call.perform();
|
||||||
|
});
|
||||||
|
this->time_->add_on_state_callback(
|
||||||
|
[this]() { this->publish_state(this->time_->hour, this->time_->minute, this->time_->second); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTTTimeComponent::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "MQTT Time '%s':", this->time_->get_name().c_str());
|
||||||
|
LOG_MQTT_COMPONENT(true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MQTTTimeComponent::component_type() const { return "time"; }
|
||||||
|
const EntityBase *MQTTTimeComponent::get_entity() const { return this->time_; }
|
||||||
|
|
||||||
|
void MQTTTimeComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
|
// Nothing extra to add here
|
||||||
|
}
|
||||||
|
bool MQTTTimeComponent::send_initial_state() {
|
||||||
|
if (this->time_->has_state()) {
|
||||||
|
return this->publish_state(this->time_->hour, this->time_->minute, this->time_->second);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) {
|
||||||
|
return this->publish_json(this->get_state_topic_(), [hour, minute, second](JsonObject root) {
|
||||||
|
root["hour"] = hour;
|
||||||
|
root["minute"] = minute;
|
||||||
|
root["second"] = second;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mqtt
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_DATETIME_TIME
|
||||||
|
#endif // USE_MQTT
|
45
esphome/components/mqtt/mqtt_time.h
Normal file
45
esphome/components/mqtt/mqtt_time.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
|
#ifdef USE_MQTT
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
|
||||||
|
#include "esphome/components/datetime/time_entity.h"
|
||||||
|
#include "mqtt_component.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace mqtt {
|
||||||
|
|
||||||
|
class MQTTTimeComponent : public mqtt::MQTTComponent {
|
||||||
|
public:
|
||||||
|
/** Construct this MQTTTimeComponent instance with the provided friendly_name and time
|
||||||
|
*
|
||||||
|
* @param time The time entity.
|
||||||
|
*/
|
||||||
|
explicit MQTTTimeComponent(datetime::TimeEntity *time);
|
||||||
|
|
||||||
|
// ========== INTERNAL METHODS ==========
|
||||||
|
// (In most use cases you won't need these)
|
||||||
|
/// Override setup.
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override;
|
||||||
|
|
||||||
|
bool send_initial_state() override;
|
||||||
|
|
||||||
|
bool publish_state(uint8_t hour, uint8_t minute, uint8_t second);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string component_type() const override;
|
||||||
|
const EntityBase *get_entity() const override;
|
||||||
|
|
||||||
|
datetime::TimeEntity *time_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mqtt
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_DATETIME_DATE
|
||||||
|
#endif // USE_MQTT
|
|
@ -4,6 +4,7 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include "esphome/core/macros.h"
|
#include "esphome/core/macros.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
#if defined(USE_ESP_IDF) || defined(USE_LIBRETINY) || USE_ARDUINO_VERSION_CODE > VERSION_CODE(3, 0, 0)
|
#if defined(USE_ESP_IDF) || defined(USE_LIBRETINY) || USE_ARDUINO_VERSION_CODE > VERSION_CODE(3, 0, 0)
|
||||||
#include <lwip/ip_addr.h>
|
#include <lwip/ip_addr.h>
|
||||||
|
@ -116,7 +117,7 @@ struct IPAddress {
|
||||||
bool is_set() { return !ip_addr_isany(&ip_addr_); }
|
bool is_set() { return !ip_addr_isany(&ip_addr_); }
|
||||||
bool is_ip4() { return IP_IS_V4(&ip_addr_); }
|
bool is_ip4() { return IP_IS_V4(&ip_addr_); }
|
||||||
bool is_ip6() { return IP_IS_V6(&ip_addr_); }
|
bool is_ip6() { return IP_IS_V6(&ip_addr_); }
|
||||||
std::string str() const { return ipaddr_ntoa(&ip_addr_); }
|
std::string str() const { return str_lower_case(ipaddr_ntoa(&ip_addr_)); }
|
||||||
bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
|
bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
|
||||||
bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
|
bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
|
||||||
IPAddress &operator+=(uint8_t increase) {
|
IPAddress &operator+=(uint8_t increase) {
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/uart/uart.h"
|
#include "esphome/components/uart/uart.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace pmsx003 {
|
namespace pmsx003 {
|
||||||
|
|
||||||
// known command bytes
|
// known command bytes
|
||||||
#define PMS_CMD_AUTO_MANUAL 0xE1 // data=0: perform measurement manually, data=1: perform measurement automatically
|
static const uint8_t PMS_CMD_AUTO_MANUAL =
|
||||||
#define PMS_CMD_TRIG_MANUAL 0xE2 // trigger a manual measurement
|
0xE1; // data=0: perform measurement manually, data=1: perform measurement automatically
|
||||||
#define PMS_CMD_ON_STANDBY 0xE4 // data=0: go to standby mode, data=1: go to normal mode
|
static const uint8_t PMS_CMD_TRIG_MANUAL = 0xE2; // trigger a manual measurement
|
||||||
|
static const uint8_t PMS_CMD_ON_STANDBY = 0xE4; // data=0: go to standby mode, data=1: go to normal mode
|
||||||
|
|
||||||
static const uint16_t PMS_STABILISING_MS = 30000; // time taken for the sensor to become stable after power on
|
static const uint16_t PMS_STABILISING_MS = 30000; // time taken for the sensor to become stable after power on
|
||||||
|
|
||||||
|
|
|
@ -72,19 +72,13 @@ void QMC5883LComponent::dump_config() {
|
||||||
LOG_SENSOR(" ", "Y Axis", this->y_sensor_);
|
LOG_SENSOR(" ", "Y Axis", this->y_sensor_);
|
||||||
LOG_SENSOR(" ", "Z Axis", this->z_sensor_);
|
LOG_SENSOR(" ", "Z Axis", this->z_sensor_);
|
||||||
LOG_SENSOR(" ", "Heading", this->heading_sensor_);
|
LOG_SENSOR(" ", "Heading", this->heading_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
}
|
}
|
||||||
float QMC5883LComponent::get_setup_priority() const { return setup_priority::DATA; }
|
float QMC5883LComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
void QMC5883LComponent::update() {
|
void QMC5883LComponent::update() {
|
||||||
uint8_t status = false;
|
uint8_t status = false;
|
||||||
this->read_byte(QMC5883L_REGISTER_STATUS, &status);
|
if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG)
|
||||||
|
this->read_byte(QMC5883L_REGISTER_STATUS, &status);
|
||||||
uint16_t raw_x, raw_y, raw_z;
|
|
||||||
if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_X_LSB, &raw_x) ||
|
|
||||||
!this->read_byte_16_(QMC5883L_REGISTER_DATA_Y_LSB, &raw_y) ||
|
|
||||||
!this->read_byte_16_(QMC5883L_REGISTER_DATA_Z_LSB, &raw_z)) {
|
|
||||||
this->status_set_warning();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float mg_per_bit;
|
float mg_per_bit;
|
||||||
switch (this->range_) {
|
switch (this->range_) {
|
||||||
|
@ -99,12 +93,49 @@ void QMC5883LComponent::update() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// in µT
|
// in µT
|
||||||
const float x = int16_t(raw_x) * mg_per_bit * 0.1f;
|
float x = NAN, y = NAN, z = NAN;
|
||||||
const float y = int16_t(raw_y) * mg_per_bit * 0.1f;
|
if (this->x_sensor_ != nullptr || this->heading_sensor_ != nullptr) {
|
||||||
const float z = int16_t(raw_z) * mg_per_bit * 0.1f;
|
uint16_t raw_x;
|
||||||
|
if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_X_LSB, &raw_x)) {
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x = int16_t(raw_x) * mg_per_bit * 0.1f;
|
||||||
|
}
|
||||||
|
if (this->y_sensor_ != nullptr || this->heading_sensor_ != nullptr) {
|
||||||
|
uint16_t raw_y;
|
||||||
|
if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_Y_LSB, &raw_y)) {
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
y = int16_t(raw_y) * mg_per_bit * 0.1f;
|
||||||
|
}
|
||||||
|
if (this->z_sensor_ != nullptr) {
|
||||||
|
uint16_t raw_z;
|
||||||
|
if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_Z_LSB, &raw_z)) {
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
z = int16_t(raw_z) * mg_per_bit * 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
float heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
|
float heading = NAN;
|
||||||
ESP_LOGD(TAG, "Got x=%0.02fµT y=%0.02fµT z=%0.02fµT heading=%0.01f° status=%u", x, y, z, heading, status);
|
if (this->heading_sensor_ != nullptr) {
|
||||||
|
heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
float temp = NAN;
|
||||||
|
if (this->temperature_sensor_ != nullptr) {
|
||||||
|
uint16_t raw_temp;
|
||||||
|
if (!this->read_byte_16_(QMC5883L_REGISTER_TEMPERATURE_LSB, &raw_temp)) {
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
temp = int16_t(raw_temp) * 0.01f;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Got x=%0.02fµT y=%0.02fµT z=%0.02fµT heading=%0.01f° temperature=%0.01f°C status=%u", x, y, z, heading,
|
||||||
|
temp, status);
|
||||||
|
|
||||||
if (this->x_sensor_ != nullptr)
|
if (this->x_sensor_ != nullptr)
|
||||||
this->x_sensor_->publish_state(x);
|
this->x_sensor_->publish_state(x);
|
||||||
|
@ -114,6 +145,8 @@ void QMC5883LComponent::update() {
|
||||||
this->z_sensor_->publish_state(z);
|
this->z_sensor_->publish_state(z);
|
||||||
if (this->heading_sensor_ != nullptr)
|
if (this->heading_sensor_ != nullptr)
|
||||||
this->heading_sensor_->publish_state(heading);
|
this->heading_sensor_->publish_state(heading);
|
||||||
|
if (this->temperature_sensor_ != nullptr)
|
||||||
|
this->temperature_sensor_->publish_state(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QMC5883LComponent::read_byte_16_(uint8_t a_register, uint16_t *data) {
|
bool QMC5883LComponent::read_byte_16_(uint8_t a_register, uint16_t *data) {
|
||||||
|
|
|
@ -40,6 +40,7 @@ class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
|
||||||
void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; }
|
void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; }
|
||||||
void set_z_sensor(sensor::Sensor *z_sensor) { z_sensor_ = z_sensor; }
|
void set_z_sensor(sensor::Sensor *z_sensor) { z_sensor_ = z_sensor; }
|
||||||
void set_heading_sensor(sensor::Sensor *heading_sensor) { heading_sensor_ = heading_sensor; }
|
void set_heading_sensor(sensor::Sensor *heading_sensor) { heading_sensor_ = heading_sensor; }
|
||||||
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QMC5883LDatarate datarate_{QMC5883L_DATARATE_10_HZ};
|
QMC5883LDatarate datarate_{QMC5883L_DATARATE_10_HZ};
|
||||||
|
@ -49,6 +50,7 @@ class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
|
||||||
sensor::Sensor *y_sensor_{nullptr};
|
sensor::Sensor *y_sensor_{nullptr};
|
||||||
sensor::Sensor *z_sensor_{nullptr};
|
sensor::Sensor *z_sensor_{nullptr};
|
||||||
sensor::Sensor *heading_sensor_{nullptr};
|
sensor::Sensor *heading_sensor_{nullptr};
|
||||||
|
sensor::Sensor *temperature_sensor_{nullptr};
|
||||||
enum ErrorCode {
|
enum ErrorCode {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
COMMUNICATION_FAILED,
|
COMMUNICATION_FAILED,
|
||||||
|
|
|
@ -6,12 +6,15 @@ from esphome.const import (
|
||||||
CONF_FIELD_STRENGTH_X,
|
CONF_FIELD_STRENGTH_X,
|
||||||
CONF_FIELD_STRENGTH_Y,
|
CONF_FIELD_STRENGTH_Y,
|
||||||
CONF_FIELD_STRENGTH_Z,
|
CONF_FIELD_STRENGTH_Z,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_OVERSAMPLING,
|
CONF_OVERSAMPLING,
|
||||||
CONF_RANGE,
|
CONF_RANGE,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
ICON_MAGNET,
|
ICON_MAGNET,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_MICROTESLA,
|
UNIT_MICROTESLA,
|
||||||
|
UNIT_CELSIUS,
|
||||||
UNIT_DEGREES,
|
UNIT_DEGREES,
|
||||||
ICON_SCREEN_ROTATION,
|
ICON_SCREEN_ROTATION,
|
||||||
CONF_UPDATE_INTERVAL,
|
CONF_UPDATE_INTERVAL,
|
||||||
|
@ -79,6 +82,12 @@ heading_schema = sensor.sensor_schema(
|
||||||
icon=ICON_SCREEN_ROTATION,
|
icon=ICON_SCREEN_ROTATION,
|
||||||
accuracy_decimals=1,
|
accuracy_decimals=1,
|
||||||
)
|
)
|
||||||
|
temperature_schema = sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
|
@ -95,6 +104,7 @@ CONFIG_SCHEMA = (
|
||||||
cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
|
cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
|
||||||
cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
|
cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
|
||||||
cv.Optional(CONF_HEADING): heading_schema,
|
cv.Optional(CONF_HEADING): heading_schema,
|
||||||
|
cv.Optional(CONF_TEMPERATURE): temperature_schema,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.polling_component_schema("60s"))
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
@ -131,3 +141,6 @@ async def to_code(config):
|
||||||
if CONF_HEADING in config:
|
if CONF_HEADING in config:
|
||||||
sens = await sensor.new_sensor(config[CONF_HEADING])
|
sens = await sensor.new_sensor(config[CONF_HEADING])
|
||||||
cg.add(var.set_heading_sensor(sens))
|
cg.add(var.set_heading_sensor(sens))
|
||||||
|
if CONF_TEMPERATURE in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||||
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
|
|
|
@ -1718,3 +1718,105 @@ async def haier_action(var, config, args):
|
||||||
vec_ = cg.std_vector.template(cg.uint8)
|
vec_ = cg.std_vector.template(cg.uint8)
|
||||||
template_ = await cg.templatable(config[CONF_CODE], args, vec_, vec_)
|
template_ = await cg.templatable(config[CONF_CODE], args, vec_, vec_)
|
||||||
cg.add(var.set_code(template_))
|
cg.add(var.set_code(template_))
|
||||||
|
|
||||||
|
|
||||||
|
# ABBWelcome
|
||||||
|
(
|
||||||
|
ABBWelcomeData,
|
||||||
|
ABBWelcomeBinarySensor,
|
||||||
|
ABBWelcomeTrigger,
|
||||||
|
ABBWelcomeAction,
|
||||||
|
ABBWelcomeDumper,
|
||||||
|
) = declare_protocol("ABBWelcome")
|
||||||
|
|
||||||
|
CONF_SOURCE_ADDRESS = "source_address"
|
||||||
|
CONF_DESTINATION_ADDRESS = "destination_address"
|
||||||
|
CONF_THREE_BYTE_ADDRESS = "three_byte_address"
|
||||||
|
CONF_MESSAGE_TYPE = "message_type"
|
||||||
|
CONF_MESSAGE_ID = "message_id"
|
||||||
|
CONF_RETRANSMISSION = "retransmission"
|
||||||
|
|
||||||
|
ABB_WELCOME_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_SOURCE_ADDRESS): cv.hex_uint32_t,
|
||||||
|
cv.Required(CONF_DESTINATION_ADDRESS): cv.hex_uint32_t,
|
||||||
|
cv.Optional(CONF_RETRANSMISSION, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_THREE_BYTE_ADDRESS, default=False): cv.boolean,
|
||||||
|
cv.Required(CONF_MESSAGE_TYPE): cv.Any(cv.hex_uint8_t, cv.uint8_t),
|
||||||
|
cv.Optional(CONF_MESSAGE_ID): cv.Any(cv.hex_uint8_t, cv.uint8_t),
|
||||||
|
cv.Optional(CONF_DATA): cv.All(
|
||||||
|
[cv.Any(cv.hex_uint8_t, cv.uint8_t)],
|
||||||
|
cv.Length(min=0, max=7),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register_binary_sensor("abbwelcome", ABBWelcomeBinarySensor, ABB_WELCOME_SCHEMA)
|
||||||
|
def abbwelcome_binary_sensor(var, config):
|
||||||
|
cg.add(var.set_three_byte_address(config[CONF_THREE_BYTE_ADDRESS]))
|
||||||
|
cg.add(var.set_source_address(config[CONF_SOURCE_ADDRESS]))
|
||||||
|
cg.add(var.set_destination_address(config[CONF_DESTINATION_ADDRESS]))
|
||||||
|
cg.add(var.set_retransmission(config[CONF_RETRANSMISSION]))
|
||||||
|
cg.add(var.set_message_type(config[CONF_MESSAGE_TYPE]))
|
||||||
|
cg.add(var.set_auto_message_id(CONF_MESSAGE_ID not in config))
|
||||||
|
if CONF_MESSAGE_ID in config:
|
||||||
|
cg.add(var.set_message_id(config[CONF_MESSAGE_ID]))
|
||||||
|
if CONF_DATA in config:
|
||||||
|
cg.add(var.set_data(config[CONF_DATA]))
|
||||||
|
cg.add(var.finalize())
|
||||||
|
|
||||||
|
|
||||||
|
@register_trigger("abbwelcome", ABBWelcomeTrigger, ABBWelcomeData)
|
||||||
|
def abbwelcome_trigger(var, config):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@register_dumper("abbwelcome", ABBWelcomeDumper)
|
||||||
|
def abbwelcome_dumper(var, config):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@register_action("abbwelcome", ABBWelcomeAction, ABB_WELCOME_SCHEMA)
|
||||||
|
async def abbwelcome_action(var, config, args):
|
||||||
|
cg.add(
|
||||||
|
var.set_three_byte_address(
|
||||||
|
await cg.templatable(config[CONF_THREE_BYTE_ADDRESS], args, cg.bool_)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cg.add(
|
||||||
|
var.set_source_address(
|
||||||
|
await cg.templatable(config[CONF_SOURCE_ADDRESS], args, cg.uint16)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cg.add(
|
||||||
|
var.set_destination_address(
|
||||||
|
await cg.templatable(config[CONF_DESTINATION_ADDRESS], args, cg.uint16)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cg.add(
|
||||||
|
var.set_retransmission(
|
||||||
|
await cg.templatable(config[CONF_RETRANSMISSION], args, cg.bool_)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cg.add(
|
||||||
|
var.set_message_type(
|
||||||
|
await cg.templatable(config[CONF_MESSAGE_TYPE], args, cg.uint8)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cg.add(var.set_auto_message_id(CONF_MESSAGE_ID not in config))
|
||||||
|
if CONF_MESSAGE_ID in config:
|
||||||
|
cg.add(
|
||||||
|
var.set_message_id(
|
||||||
|
await cg.templatable(config[CONF_MESSAGE_ID], args, cg.uint8)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if CONF_DATA in config:
|
||||||
|
data_ = config[CONF_DATA]
|
||||||
|
if cg.is_template(data_):
|
||||||
|
template_ = await cg.templatable(
|
||||||
|
data_, args, cg.std_vector.template(cg.uint8)
|
||||||
|
)
|
||||||
|
cg.add(var.set_data_template(template_))
|
||||||
|
else:
|
||||||
|
cg.add(var.set_data_static(data_))
|
||||||
|
|
123
esphome/components/remote_base/abbwelcome_protocol.cpp
Normal file
123
esphome/components/remote_base/abbwelcome_protocol.cpp
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#include "abbwelcome_protocol.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace remote_base {
|
||||||
|
|
||||||
|
static const char *const TAG = "remote.abbwelcome";
|
||||||
|
|
||||||
|
static const uint32_t BIT_ONE_SPACE_US = 102;
|
||||||
|
static const uint32_t BIT_ZERO_MARK_US = 32; // 18-44
|
||||||
|
static const uint32_t BIT_ZERO_SPACE_US = BIT_ONE_SPACE_US - BIT_ZERO_MARK_US;
|
||||||
|
static const uint16_t BYTE_SPACE_US = 210;
|
||||||
|
|
||||||
|
uint8_t ABBWelcomeData::calc_cs_() const {
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
for (uint8_t i = 0; i < this->size() - 1; i++) {
|
||||||
|
uint16_t temp = checksum ^ (this->data_[i]);
|
||||||
|
temp = temp ^ (uint16_t) (((uint32_t) temp << 0x11) >> 0x10) ^ (uint16_t) (((uint32_t) temp << 0x12) >> 0x10) ^
|
||||||
|
(uint16_t) (((uint32_t) temp << 0x13) >> 0x10) ^ (uint16_t) (((uint32_t) temp << 0x14) >> 0x10) ^
|
||||||
|
(uint16_t) (((uint32_t) temp << 0x15) >> 0x10) ^ (uint16_t) (((uint32_t) temp << 0x16) >> 0x10) ^
|
||||||
|
(uint16_t) (((uint32_t) temp << 0x17) >> 0x10);
|
||||||
|
checksum = (temp & 0xfe) ^ ((temp >> 8) & 1);
|
||||||
|
}
|
||||||
|
return ~checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABBWelcomeProtocol::encode_byte_(RemoteTransmitData *dst, uint8_t data) const {
|
||||||
|
// space = bus high, mark = activate bus pulldown
|
||||||
|
dst->mark(BIT_ZERO_MARK_US);
|
||||||
|
uint32_t next_space = BIT_ZERO_SPACE_US;
|
||||||
|
for (uint8_t mask = 1 << 7; mask; mask >>= 1) {
|
||||||
|
if (data & mask) {
|
||||||
|
next_space += BIT_ONE_SPACE_US;
|
||||||
|
} else {
|
||||||
|
dst->space(next_space);
|
||||||
|
dst->mark(BIT_ZERO_MARK_US);
|
||||||
|
next_space = BIT_ZERO_SPACE_US;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_space += BYTE_SPACE_US;
|
||||||
|
dst->space(next_space);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABBWelcomeProtocol::encode(RemoteTransmitData *dst, const ABBWelcomeData &src) {
|
||||||
|
dst->set_carrier_frequency(0);
|
||||||
|
uint32_t reserve_count = 0;
|
||||||
|
for (size_t i = 0; i < src.size(); i++) {
|
||||||
|
reserve_count += 2 * (9 - (src[i] & 1) - ((src[i] >> 1) & 1) - ((src[i] >> 2) & 1) - ((src[i] >> 3) & 1) -
|
||||||
|
((src[i] >> 4) & 1) - ((src[i] >> 5) & 1) - ((src[i] >> 6) & 1) - ((src[i] >> 7) & 1));
|
||||||
|
}
|
||||||
|
dst->reserve(reserve_count);
|
||||||
|
for (size_t i = 0; i < src.size(); i++)
|
||||||
|
this->encode_byte_(dst, src[i]);
|
||||||
|
ESP_LOGD(TAG, "Transmitting: %s", src.to_string().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ABBWelcomeProtocol::decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data) {
|
||||||
|
if (!src.expect_mark(BIT_ZERO_MARK_US))
|
||||||
|
return false;
|
||||||
|
uint32_t next_space = BIT_ZERO_SPACE_US;
|
||||||
|
for (uint8_t mask = 1 << 7; mask; mask >>= 1) {
|
||||||
|
// if (!src.peek_space_at_least(next_space, 0))
|
||||||
|
// return false;
|
||||||
|
if (src.expect_space(next_space)) {
|
||||||
|
if (!src.expect_mark(BIT_ZERO_MARK_US))
|
||||||
|
return false;
|
||||||
|
next_space = BIT_ZERO_SPACE_US;
|
||||||
|
} else {
|
||||||
|
data |= mask;
|
||||||
|
next_space += BIT_ONE_SPACE_US;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_space += BYTE_SPACE_US;
|
||||||
|
// if (!src.peek_space_at_least(next_space, 0))
|
||||||
|
// return false;
|
||||||
|
done = !(src.expect_space(next_space));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<ABBWelcomeData> ABBWelcomeProtocol::decode(RemoteReceiveData src) {
|
||||||
|
if (src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US) &&
|
||||||
|
src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US) &&
|
||||||
|
src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US) &&
|
||||||
|
src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US) &&
|
||||||
|
src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + BIT_ONE_SPACE_US + BYTE_SPACE_US) &&
|
||||||
|
src.expect_item(BIT_ZERO_MARK_US, BIT_ZERO_SPACE_US + 8 * BIT_ONE_SPACE_US + BYTE_SPACE_US)) {
|
||||||
|
ESP_LOGVV(TAG, "Received Header: 0x55FF");
|
||||||
|
ABBWelcomeData out;
|
||||||
|
out[0] = 0x55;
|
||||||
|
out[1] = 0xff;
|
||||||
|
bool done = false;
|
||||||
|
uint8_t length = 10;
|
||||||
|
uint8_t received_bytes = 2;
|
||||||
|
for (; (received_bytes < length) && !done; received_bytes++) {
|
||||||
|
uint8_t data = 0;
|
||||||
|
if (!this->decode_byte_(src, done, data)) {
|
||||||
|
ESP_LOGW(TAG, "Received incomplete packet: %s", out.to_string(received_bytes).c_str());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (received_bytes == 2) {
|
||||||
|
length += std::min(static_cast<uint8_t>(data & DATA_LENGTH_MASK), MAX_DATA_LENGTH);
|
||||||
|
if (data & 0x40) {
|
||||||
|
length += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGVV(TAG, "Received Byte: 0x%02X", data);
|
||||||
|
out[received_bytes] = data;
|
||||||
|
}
|
||||||
|
if (out.is_valid()) {
|
||||||
|
ESP_LOGI(TAG, "Received: %s", out.to_string().c_str());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "Received malformed packet: %s", out.to_string(received_bytes).c_str());
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ABBWelcomeProtocol::dump(const ABBWelcomeData &data) {
|
||||||
|
ESP_LOGD(TAG, "Received ABBWelcome: %s", data.to_string().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace remote_base
|
||||||
|
} // namespace esphome
|
251
esphome/components/remote_base/abbwelcome_protocol.h
Normal file
251
esphome/components/remote_base/abbwelcome_protocol.h
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "remote_base.h"
|
||||||
|
#include <array>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace remote_base {
|
||||||
|
|
||||||
|
static const uint8_t MAX_DATA_LENGTH = 15;
|
||||||
|
static const uint8_t DATA_LENGTH_MASK = 0x3f;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Message Format:
|
||||||
|
2 bytes: Sync (0x55FF)
|
||||||
|
1 bit: Retransmission flag (High means retransmission)
|
||||||
|
1 bit: Address length flag (Low means 2 bytes, High means 3 bytes)
|
||||||
|
2 bits: Unknown
|
||||||
|
4 bits: Data length (in bytes)
|
||||||
|
1 bit: Reply flag (High means this is a reply to a previous message with the same message type)
|
||||||
|
7 bits: Message type
|
||||||
|
2-3 bytes: Destination address
|
||||||
|
2-3 bytes: Source address
|
||||||
|
1 byte: Message ID (randomized, does not change for retransmissions)
|
||||||
|
0-? bytes: Data
|
||||||
|
1 byte: Checksum
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ABBWelcomeData {
|
||||||
|
public:
|
||||||
|
// Make default
|
||||||
|
ABBWelcomeData() {
|
||||||
|
std::fill(std::begin(this->data_), std::end(this->data_), 0);
|
||||||
|
this->data_[0] = 0x55;
|
||||||
|
this->data_[1] = 0xff;
|
||||||
|
}
|
||||||
|
// Make from initializer_list
|
||||||
|
ABBWelcomeData(std::initializer_list<uint8_t> data) {
|
||||||
|
std::fill(std::begin(this->data_), std::end(this->data_), 0);
|
||||||
|
std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin());
|
||||||
|
}
|
||||||
|
// Make from vector
|
||||||
|
ABBWelcomeData(const std::vector<uint8_t> &data) {
|
||||||
|
std::fill(std::begin(this->data_), std::end(this->data_), 0);
|
||||||
|
std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin());
|
||||||
|
}
|
||||||
|
// Default copy constructor
|
||||||
|
ABBWelcomeData(const ABBWelcomeData &) = default;
|
||||||
|
|
||||||
|
bool auto_message_id{false};
|
||||||
|
|
||||||
|
uint8_t *data() { return this->data_.data(); }
|
||||||
|
const uint8_t *data() const { return this->data_.data(); }
|
||||||
|
uint8_t size() const {
|
||||||
|
return std::min(static_cast<uint8_t>(6 + (2 * this->get_address_length()) + (this->data_[2] & DATA_LENGTH_MASK)),
|
||||||
|
static_cast<uint8_t>(this->data_.size()));
|
||||||
|
}
|
||||||
|
bool is_valid() const {
|
||||||
|
return this->data_[0] == 0x55 && this->data_[1] == 0xff &&
|
||||||
|
((this->data_[2] & DATA_LENGTH_MASK) <= MAX_DATA_LENGTH) &&
|
||||||
|
(this->data_[this->size() - 1] == this->calc_cs_());
|
||||||
|
}
|
||||||
|
void set_retransmission(bool retransmission) {
|
||||||
|
if (retransmission) {
|
||||||
|
this->data_[2] |= 0x80;
|
||||||
|
} else {
|
||||||
|
this->data_[2] &= 0x7f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool get_retransmission() const { return this->data_[2] & 0x80; }
|
||||||
|
// set_three_byte_address must be called before set_source_address, set_destination_address, set_message_id and
|
||||||
|
// set_data!
|
||||||
|
void set_three_byte_address(bool three_byte_address) {
|
||||||
|
if (three_byte_address) {
|
||||||
|
this->data_[2] |= 0x40;
|
||||||
|
} else {
|
||||||
|
this->data_[2] &= 0xbf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t get_three_byte_address() const { return (this->data_[2] & 0x40); }
|
||||||
|
uint8_t get_address_length() const { return this->get_three_byte_address() ? 3 : 2; }
|
||||||
|
void set_message_type(uint8_t message_type) { this->data_[3] = message_type; }
|
||||||
|
uint8_t get_message_type() const { return this->data_[3]; }
|
||||||
|
void set_destination_address(uint32_t address) {
|
||||||
|
if (this->get_address_length() == 2) {
|
||||||
|
this->data_[4] = (address >> 8) & 0xff;
|
||||||
|
this->data_[5] = address & 0xff;
|
||||||
|
} else {
|
||||||
|
this->data_[4] = (address >> 16) & 0xff;
|
||||||
|
this->data_[5] = (address >> 8) & 0xff;
|
||||||
|
this->data_[6] = address & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t get_destination_address() const {
|
||||||
|
if (this->get_address_length() == 2) {
|
||||||
|
return (this->data_[4] << 8) + this->data_[5];
|
||||||
|
}
|
||||||
|
return (this->data_[4] << 16) + (this->data_[5] << 8) + this->data_[6];
|
||||||
|
}
|
||||||
|
void set_source_address(uint32_t address) {
|
||||||
|
if (this->get_address_length() == 2) {
|
||||||
|
this->data_[6] = (address >> 8) & 0xff;
|
||||||
|
this->data_[7] = address & 0xff;
|
||||||
|
} else {
|
||||||
|
this->data_[7] = (address >> 16) & 0xff;
|
||||||
|
this->data_[8] = (address >> 8) & 0xff;
|
||||||
|
this->data_[9] = address & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t get_source_address() const {
|
||||||
|
if (this->get_address_length() == 2) {
|
||||||
|
return (this->data_[6] << 8) + this->data_[7];
|
||||||
|
}
|
||||||
|
return (this->data_[7] << 16) + (this->data_[8] << 8) + this->data_[9];
|
||||||
|
}
|
||||||
|
void set_message_id(uint8_t message_id) { this->data_[4 + 2 * this->get_address_length()] = message_id; }
|
||||||
|
uint8_t get_message_id() const { return this->data_[4 + 2 * this->get_address_length()]; }
|
||||||
|
void set_data(std::vector<uint8_t> data) {
|
||||||
|
uint8_t size = std::min(MAX_DATA_LENGTH, static_cast<uint8_t>(data.size()));
|
||||||
|
this->data_[2] &= (0xff ^ DATA_LENGTH_MASK);
|
||||||
|
this->data_[2] |= (size & DATA_LENGTH_MASK);
|
||||||
|
if (size)
|
||||||
|
std::copy_n(data.begin(), size, this->data_.begin() + 5 + 2 * this->get_address_length());
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> get_data() const {
|
||||||
|
std::vector<uint8_t> data(this->data_.begin() + 5 + 2 * this->get_address_length(),
|
||||||
|
this->data_.begin() + 5 + 2 * this->get_address_length() + this->get_data_size());
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
uint8_t get_data_size() const {
|
||||||
|
return std::min(MAX_DATA_LENGTH, static_cast<uint8_t>(this->data_[2] & DATA_LENGTH_MASK));
|
||||||
|
}
|
||||||
|
void finalize() {
|
||||||
|
if (this->auto_message_id && !this->get_retransmission() && !(this->data_[3] & 0x80)) {
|
||||||
|
this->set_message_id(static_cast<uint8_t>(random_uint32()));
|
||||||
|
}
|
||||||
|
this->data_[0] = 0x55;
|
||||||
|
this->data_[1] = 0xff;
|
||||||
|
this->data_[this->size() - 1] = this->calc_cs_();
|
||||||
|
}
|
||||||
|
std::string to_string(uint8_t max_print_bytes = 255) const {
|
||||||
|
std::string info;
|
||||||
|
if (this->is_valid()) {
|
||||||
|
info = str_sprintf(this->get_three_byte_address() ? "[%06X %s %06X] Type: %02X" : "[%04X %s %04X] Type: %02X",
|
||||||
|
this->get_source_address(), this->get_retransmission() ? "»" : ">",
|
||||||
|
this->get_destination_address(), this->get_message_type());
|
||||||
|
if (this->get_data_size())
|
||||||
|
info += str_sprintf(", Data: %s", format_hex_pretty(this->get_data()).c_str());
|
||||||
|
} else {
|
||||||
|
info = "[Invalid]";
|
||||||
|
}
|
||||||
|
uint8_t print_bytes = std::min(this->size(), max_print_bytes);
|
||||||
|
if (print_bytes)
|
||||||
|
info = str_sprintf("%s %s", format_hex_pretty(this->data_.data(), print_bytes).c_str(), info.c_str());
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
bool operator==(const ABBWelcomeData &rhs) const {
|
||||||
|
if (std::equal(this->data_.begin(), this->data_.begin() + this->size(), rhs.data_.begin()))
|
||||||
|
return true;
|
||||||
|
return (this->auto_message_id || rhs.auto_message_id) && this->is_valid() && rhs.is_valid() &&
|
||||||
|
(this->get_message_type() == rhs.get_message_type()) &&
|
||||||
|
(this->get_source_address() == rhs.get_source_address()) &&
|
||||||
|
(this->get_destination_address() == rhs.get_destination_address()) && (this->get_data() == rhs.get_data());
|
||||||
|
}
|
||||||
|
uint8_t &operator[](size_t idx) { return this->data_[idx]; }
|
||||||
|
const uint8_t &operator[](size_t idx) const { return this->data_[idx]; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::array<uint8_t, 12 + MAX_DATA_LENGTH> data_;
|
||||||
|
// Calculate checksum
|
||||||
|
uint8_t calc_cs_() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ABBWelcomeProtocol : public RemoteProtocol<ABBWelcomeData> {
|
||||||
|
public:
|
||||||
|
void encode(RemoteTransmitData *dst, const ABBWelcomeData &src) override;
|
||||||
|
optional<ABBWelcomeData> decode(RemoteReceiveData src) override;
|
||||||
|
void dump(const ABBWelcomeData &data) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void encode_byte_(RemoteTransmitData *dst, uint8_t data) const;
|
||||||
|
bool decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ABBWelcomeBinarySensor : public RemoteReceiverBinarySensorBase {
|
||||||
|
public:
|
||||||
|
bool matches(RemoteReceiveData src) override {
|
||||||
|
auto data = ABBWelcomeProtocol().decode(src);
|
||||||
|
return data.has_value() && data.value() == this->data_;
|
||||||
|
}
|
||||||
|
void set_source_address(const uint32_t source_address) { this->data_.set_source_address(source_address); }
|
||||||
|
void set_destination_address(const uint32_t destination_address) {
|
||||||
|
this->data_.set_destination_address(destination_address);
|
||||||
|
}
|
||||||
|
void set_retransmission(const bool retransmission) { this->data_.set_retransmission(retransmission); }
|
||||||
|
void set_three_byte_address(const bool three_byte_address) { this->data_.set_three_byte_address(three_byte_address); }
|
||||||
|
void set_message_type(const uint8_t message_type) { this->data_.set_message_type(message_type); }
|
||||||
|
void set_message_id(const uint8_t message_id) { this->data_.set_message_id(message_id); }
|
||||||
|
void set_auto_message_id(const bool auto_message_id) { this->data_.auto_message_id = auto_message_id; }
|
||||||
|
void set_data(const std::vector<uint8_t> &data) { this->data_.set_data(data); }
|
||||||
|
void finalize() { this->data_.finalize(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ABBWelcomeData data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ABBWelcomeTrigger = RemoteReceiverTrigger<ABBWelcomeProtocol>;
|
||||||
|
using ABBWelcomeDumper = RemoteReceiverDumper<ABBWelcomeProtocol>;
|
||||||
|
|
||||||
|
template<typename... Ts> class ABBWelcomeAction : public RemoteTransmitterActionBase<Ts...> {
|
||||||
|
TEMPLATABLE_VALUE(uint32_t, source_address)
|
||||||
|
TEMPLATABLE_VALUE(uint32_t, destination_address)
|
||||||
|
TEMPLATABLE_VALUE(bool, retransmission)
|
||||||
|
TEMPLATABLE_VALUE(bool, three_byte_address)
|
||||||
|
TEMPLATABLE_VALUE(uint8_t, message_type)
|
||||||
|
TEMPLATABLE_VALUE(uint8_t, message_id)
|
||||||
|
TEMPLATABLE_VALUE(bool, auto_message_id)
|
||||||
|
void set_data_static(std::vector<uint8_t> data) { data_static_ = std::move(data); }
|
||||||
|
void set_data_template(std::function<std::vector<uint8_t>(Ts...)> func) {
|
||||||
|
this->data_func_ = func;
|
||||||
|
has_data_func_ = true;
|
||||||
|
}
|
||||||
|
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||||
|
ABBWelcomeData data;
|
||||||
|
data.set_three_byte_address(this->three_byte_address_.value(x...));
|
||||||
|
data.set_source_address(this->source_address_.value(x...));
|
||||||
|
data.set_destination_address(this->destination_address_.value(x...));
|
||||||
|
data.set_retransmission(this->retransmission_.value(x...));
|
||||||
|
data.set_message_type(this->message_type_.value(x...));
|
||||||
|
data.set_message_id(this->message_id_.value(x...));
|
||||||
|
data.auto_message_id = this->auto_message_id_.value(x...);
|
||||||
|
if (has_data_func_) {
|
||||||
|
data.set_data(this->data_func_(x...));
|
||||||
|
} else {
|
||||||
|
data.set_data(this->data_static_);
|
||||||
|
}
|
||||||
|
data.finalize();
|
||||||
|
ABBWelcomeProtocol().encode(dst, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
|
||||||
|
std::vector<uint8_t> data_static_{};
|
||||||
|
bool has_data_func_{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace remote_base
|
||||||
|
} // namespace esphome
|
|
@ -15,6 +15,9 @@ RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_b
|
||||||
next_rmt_channel = rmt_channel_t(int(next_rmt_channel) + mem_block_num);
|
next_rmt_channel = rmt_channel_t(int(next_rmt_channel) + mem_block_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RemoteRMTChannel::RemoteRMTChannel(rmt_channel_t channel, uint8_t mem_block_num)
|
||||||
|
: channel_(channel), mem_block_num_(mem_block_num) {}
|
||||||
|
|
||||||
void RemoteRMTChannel::config_rmt(rmt_config_t &rmt) {
|
void RemoteRMTChannel::config_rmt(rmt_config_t &rmt) {
|
||||||
if (rmt_channel_t(int(this->channel_) + this->mem_block_num_) > RMT_CHANNEL_MAX) {
|
if (rmt_channel_t(int(this->channel_) + this->mem_block_num_) > RMT_CHANNEL_MAX) {
|
||||||
this->mem_block_num_ = int(RMT_CHANNEL_MAX) - int(this->channel_);
|
this->mem_block_num_ = int(RMT_CHANNEL_MAX) - int(this->channel_);
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/automation.h"
|
|
||||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
#include <driver/rmt.h>
|
#include <driver/rmt.h>
|
||||||
|
@ -86,6 +86,7 @@ class RemoteComponentBase {
|
||||||
class RemoteRMTChannel {
|
class RemoteRMTChannel {
|
||||||
public:
|
public:
|
||||||
explicit RemoteRMTChannel(uint8_t mem_block_num = 1);
|
explicit RemoteRMTChannel(uint8_t mem_block_num = 1);
|
||||||
|
explicit RemoteRMTChannel(rmt_channel_t channel, uint8_t mem_block_num = 1);
|
||||||
|
|
||||||
void config_rmt(rmt_config_t &rmt);
|
void config_rmt(rmt_config_t &rmt);
|
||||||
void set_clock_divider(uint8_t clock_divider) { this->clock_divider_ = clock_divider; }
|
void set_clock_divider(uint8_t clock_divider) { this->clock_divider_ = clock_divider; }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.components import remote_base
|
from esphome.components import remote_base, esp32_rmt
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_BUFFER_SIZE,
|
CONF_BUFFER_SIZE,
|
||||||
CONF_DUMP,
|
CONF_DUMP,
|
||||||
|
@ -11,6 +11,7 @@ from esphome.const import (
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
CONF_TOLERANCE,
|
CONF_TOLERANCE,
|
||||||
CONF_MEMORY_BLOCKS,
|
CONF_MEMORY_BLOCKS,
|
||||||
|
CONF_RMT_CHANNEL,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, TimePeriod
|
from esphome.core import CORE, TimePeriod
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
|
||||||
CONF_IDLE, default="10ms"
|
CONF_IDLE, default="10ms"
|
||||||
): cv.positive_time_period_microseconds,
|
): cv.positive_time_period_microseconds,
|
||||||
cv.Optional(CONF_MEMORY_BLOCKS, default=3): cv.Range(min=1, max=8),
|
cv.Optional(CONF_MEMORY_BLOCKS, default=3): cv.Range(min=1, max=8),
|
||||||
|
cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=False),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
)
|
)
|
||||||
|
@ -53,7 +55,12 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32:
|
||||||
var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS])
|
if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None:
|
||||||
|
var = cg.new_Pvariable(
|
||||||
|
config[CONF_ID], pin, rmt_channel, config[CONF_MEMORY_BLOCKS]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS])
|
||||||
else:
|
else:
|
||||||
var = cg.new_Pvariable(config[CONF_ID], pin)
|
var = cg.new_Pvariable(config[CONF_ID], pin)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/remote_base/remote_base.h"
|
#include "esphome/components/remote_base/remote_base.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
|
@ -38,6 +38,9 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
RemoteReceiverComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1)
|
RemoteReceiverComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1)
|
||||||
: RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {}
|
: RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {}
|
||||||
|
|
||||||
|
RemoteReceiverComponent(InternalGPIOPin *pin, rmt_channel_t channel, uint8_t mem_block_num = 1)
|
||||||
|
: RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(channel, mem_block_num) {}
|
||||||
#else
|
#else
|
||||||
RemoteReceiverComponent(InternalGPIOPin *pin) : RemoteReceiverBase(pin) {}
|
RemoteReceiverComponent(InternalGPIOPin *pin) : RemoteReceiverBase(pin) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.components import remote_base
|
from esphome.components import remote_base, esp32_rmt
|
||||||
from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN
|
from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, CONF_RMT_CHANNEL
|
||||||
|
|
||||||
AUTO_LOAD = ["remote_base"]
|
AUTO_LOAD = ["remote_base"]
|
||||||
remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter")
|
remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter")
|
||||||
|
@ -18,13 +18,17 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All(
|
cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All(
|
||||||
cv.percentage_int, cv.Range(min=1, max=100)
|
cv.percentage_int, cv.Range(min=1, max=100)
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||||
var = cg.new_Pvariable(config[CONF_ID], pin)
|
if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None:
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID], pin, rmt_channel)
|
||||||
|
else:
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID], pin)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
cg.add(var.set_carrier_duty_percent(config[CONF_CARRIER_DUTY_PERCENT]))
|
cg.add(var.set_carrier_duty_percent(config[CONF_CARRIER_DUTY_PERCENT]))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/remote_base/remote_base.h"
|
#include "esphome/components/remote_base/remote_base.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -16,8 +16,15 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit RemoteTransmitterComponent(InternalGPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {}
|
#ifdef USE_ESP32
|
||||||
|
RemoteTransmitterComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1)
|
||||||
|
: remote_base::RemoteTransmitterBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {}
|
||||||
|
|
||||||
|
RemoteTransmitterComponent(InternalGPIOPin *pin, rmt_channel_t channel, uint8_t mem_block_num = 1)
|
||||||
|
: remote_base::RemoteTransmitterBase(pin), remote_base::RemoteRMTChannel(channel, mem_block_num) {}
|
||||||
|
#else
|
||||||
|
explicit RemoteTransmitterComponent(InternalGPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {}
|
||||||
|
#endif
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
|
@ -74,12 +74,12 @@ def _format_framework_arduino_version(ver: cv.Version) -> str:
|
||||||
# The default/recommended arduino framework version
|
# The default/recommended arduino framework version
|
||||||
# - https://github.com/earlephilhower/arduino-pico/releases
|
# - https://github.com/earlephilhower/arduino-pico/releases
|
||||||
# - https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico
|
# - https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico
|
||||||
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 6, 0)
|
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 7, 2)
|
||||||
|
|
||||||
# The platformio/raspberrypi version to use for arduino frameworks
|
# The platformio/raspberrypi version to use for arduino frameworks
|
||||||
# - https://github.com/platformio/platform-raspberrypi/releases
|
# - https://github.com/platformio/platform-raspberrypi/releases
|
||||||
# - https://api.registry.platformio.org/v3/packages/platformio/platform/raspberrypi
|
# - https://api.registry.platformio.org/v3/packages/platformio/platform/raspberrypi
|
||||||
ARDUINO_PLATFORM_VERSION = cv.Version(1, 10, 0)
|
ARDUINO_PLATFORM_VERSION = cv.Version(1, 12, 0)
|
||||||
|
|
||||||
|
|
||||||
def _arduino_check_versions(value):
|
def _arduino_check_versions(value):
|
||||||
|
|
|
@ -36,6 +36,10 @@ RTL87XX_BOARDS = {
|
||||||
"name": "T103_V1.0",
|
"name": "T103_V1.0",
|
||||||
"family": FAMILY_RTL8710B,
|
"family": FAMILY_RTL8710B,
|
||||||
},
|
},
|
||||||
|
"t112-v1.1": {
|
||||||
|
"name": "T112_V1.1",
|
||||||
|
"family": FAMILY_RTL8710B,
|
||||||
|
},
|
||||||
"wr1": {
|
"wr1": {
|
||||||
"name": "WR1 Wi-Fi Module",
|
"name": "WR1 Wi-Fi Module",
|
||||||
"family": FAMILY_RTL8710B,
|
"family": FAMILY_RTL8710B,
|
||||||
|
@ -125,7 +129,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 23,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -136,9 +139,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 22,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 30,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -180,11 +181,9 @@ RTL87XX_BOARD_PINS = {
|
||||||
"SERIAL2_RTS": 20,
|
"SERIAL2_RTS": 20,
|
||||||
"SERIAL2_RX": 15,
|
"SERIAL2_RX": 15,
|
||||||
"SERIAL2_TX": 16,
|
"SERIAL2_TX": 16,
|
||||||
"CS0": 15,
|
|
||||||
"CTS1": 4,
|
"CTS1": 4,
|
||||||
"CTS2": 19,
|
"CTS2": 19,
|
||||||
"MISO0": 20,
|
"MISO0": 20,
|
||||||
"MOSI0": 19,
|
|
||||||
"PA00": 0,
|
"PA00": 0,
|
||||||
"PA0": 0,
|
"PA0": 0,
|
||||||
"PA01": 1,
|
"PA01": 1,
|
||||||
|
@ -203,23 +202,15 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA18": 18,
|
"PA18": 18,
|
||||||
"PA19": 19,
|
"PA19": 19,
|
||||||
"PA20": 20,
|
"PA20": 20,
|
||||||
"PWM0": 0,
|
|
||||||
"PWM1": 1,
|
"PWM1": 1,
|
||||||
"PWM2": 14,
|
|
||||||
"PWM3": 3,
|
|
||||||
"PWM4": 16,
|
|
||||||
"PWM5": 17,
|
"PWM5": 17,
|
||||||
"PWM6": 18,
|
"PWM6": 18,
|
||||||
"PWM7": 13,
|
|
||||||
"RTS2": 20,
|
"RTS2": 20,
|
||||||
"RX0": 13,
|
"RX0": 13,
|
||||||
"RX1": 0,
|
|
||||||
"RX2": 15,
|
"RX2": 15,
|
||||||
"SCK0": 3,
|
|
||||||
"SCL0": 19,
|
"SCL0": 19,
|
||||||
"SDA0": 3,
|
"SDA0": 3,
|
||||||
"TX0": 14,
|
"TX0": 14,
|
||||||
"TX1": 1,
|
|
||||||
"TX2": 16,
|
"TX2": 16,
|
||||||
"D0": 17,
|
"D0": 17,
|
||||||
"D1": 18,
|
"D1": 18,
|
||||||
|
@ -294,7 +285,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 23,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -305,9 +295,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 29,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 30,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -390,7 +378,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 23,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -401,9 +388,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 29,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 30,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -485,7 +470,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 23,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -496,9 +480,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 29,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 30,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -560,7 +542,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"CTS0": 10,
|
"CTS0": 10,
|
||||||
"CTS1": 4,
|
"CTS1": 4,
|
||||||
"CTS2": 19,
|
"CTS2": 19,
|
||||||
"MISO0": 20,
|
|
||||||
"MOSI0": 19,
|
"MOSI0": 19,
|
||||||
"PA00": 0,
|
"PA00": 0,
|
||||||
"PA0": 0,
|
"PA0": 0,
|
||||||
|
@ -591,23 +572,13 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA20": 20,
|
"PA20": 20,
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PWM0": 20,
|
"PWM0": 20,
|
||||||
"PWM1": 12,
|
|
||||||
"PWM2": 14,
|
|
||||||
"PWM3": 15,
|
|
||||||
"PWM4": 16,
|
|
||||||
"PWM5": 17,
|
"PWM5": 17,
|
||||||
"PWM6": 18,
|
"PWM6": 18,
|
||||||
"PWM7": 23,
|
"PWM7": 23,
|
||||||
"RTS0": 9,
|
"RTS0": 9,
|
||||||
"RTS2": 20,
|
"RTS2": 20,
|
||||||
"RX0": 13,
|
|
||||||
"RX1": 2,
|
|
||||||
"RX2": 15,
|
"RX2": 15,
|
||||||
"SCK0": 16,
|
"SCK0": 16,
|
||||||
"SCL0": 19,
|
|
||||||
"SDA0": 20,
|
|
||||||
"TX0": 14,
|
|
||||||
"TX1": 3,
|
|
||||||
"TX2": 16,
|
"TX2": 16,
|
||||||
"D0": 0,
|
"D0": 0,
|
||||||
"D1": 1,
|
"D1": 1,
|
||||||
|
@ -652,7 +623,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 14,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -720,7 +690,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 23,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -731,9 +700,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 29,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 30,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -751,6 +718,75 @@ RTL87XX_BOARD_PINS = {
|
||||||
"A0": 19,
|
"A0": 19,
|
||||||
"A1": 41,
|
"A1": 41,
|
||||||
},
|
},
|
||||||
|
"t112-v1.1": {
|
||||||
|
"SPI0_CS": 19,
|
||||||
|
"SPI0_MISO": 22,
|
||||||
|
"SPI0_MOSI": 23,
|
||||||
|
"SPI0_SCK": 18,
|
||||||
|
"SPI1_CS": 19,
|
||||||
|
"SPI1_MISO": 22,
|
||||||
|
"SPI1_MOSI": 23,
|
||||||
|
"SPI1_SCK": 18,
|
||||||
|
"WIRE0_SCL_0": 29,
|
||||||
|
"WIRE0_SCL_1": 22,
|
||||||
|
"WIRE0_SDA_0": 19,
|
||||||
|
"WIRE0_SDA_1": 30,
|
||||||
|
"WIRE1_SCL": 18,
|
||||||
|
"WIRE1_SDA": 23,
|
||||||
|
"SERIAL0_CTS": 19,
|
||||||
|
"SERIAL0_RTS": 22,
|
||||||
|
"SERIAL0_RX": 18,
|
||||||
|
"SERIAL0_TX": 23,
|
||||||
|
"SERIAL2_RX": 29,
|
||||||
|
"SERIAL2_TX": 30,
|
||||||
|
"ADC1": 19,
|
||||||
|
"CS0": 19,
|
||||||
|
"CS1": 19,
|
||||||
|
"CTS0": 19,
|
||||||
|
"MISO0": 22,
|
||||||
|
"MISO1": 22,
|
||||||
|
"MOSI0": 23,
|
||||||
|
"MOSI1": 23,
|
||||||
|
"PA00": 0,
|
||||||
|
"PA0": 0,
|
||||||
|
"PA05": 5,
|
||||||
|
"PA5": 5,
|
||||||
|
"PA12": 12,
|
||||||
|
"PA14": 14,
|
||||||
|
"PA15": 15,
|
||||||
|
"PA18": 18,
|
||||||
|
"PA19": 19,
|
||||||
|
"PA22": 22,
|
||||||
|
"PA23": 23,
|
||||||
|
"PA29": 29,
|
||||||
|
"PA30": 30,
|
||||||
|
"PWM1": 15,
|
||||||
|
"PWM2": 0,
|
||||||
|
"PWM3": 12,
|
||||||
|
"PWM4": 30,
|
||||||
|
"PWM5": 22,
|
||||||
|
"RTS0": 22,
|
||||||
|
"RX0": 18,
|
||||||
|
"RX2": 29,
|
||||||
|
"SCK0": 18,
|
||||||
|
"SCK1": 18,
|
||||||
|
"SCL1": 18,
|
||||||
|
"SDA1": 23,
|
||||||
|
"TX0": 23,
|
||||||
|
"TX2": 30,
|
||||||
|
"D0": 29,
|
||||||
|
"D1": 19,
|
||||||
|
"D2": 15,
|
||||||
|
"D3": 14,
|
||||||
|
"D4": 0,
|
||||||
|
"D5": 5,
|
||||||
|
"D6": 18,
|
||||||
|
"D7": 12,
|
||||||
|
"D8": 23,
|
||||||
|
"D9": 22,
|
||||||
|
"D10": 30,
|
||||||
|
"A0": 19,
|
||||||
|
},
|
||||||
"wr1": {
|
"wr1": {
|
||||||
"SPI0_CS": 19,
|
"SPI0_CS": 19,
|
||||||
"SPI0_MISO": 22,
|
"SPI0_MISO": 22,
|
||||||
|
@ -793,7 +829,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 14,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM4": 29,
|
"PWM4": 29,
|
||||||
|
@ -803,9 +838,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 22,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 19,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -863,7 +896,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 14,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
"PWM4": 29,
|
"PWM4": 29,
|
||||||
|
@ -873,9 +905,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 22,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 19,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -915,7 +945,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 14,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -969,7 +998,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 14,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
"PWM4": 29,
|
"PWM4": 29,
|
||||||
|
@ -979,7 +1007,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 29,
|
"SCL0": 29,
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 30,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -1083,7 +1110,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 23,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -1094,9 +1120,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 29,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 30,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -1157,7 +1181,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 23,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -1168,9 +1191,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 22,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 19,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -1231,7 +1252,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 23,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -1242,9 +1262,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 29,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 30,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -1305,7 +1323,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 23,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
@ -1316,9 +1333,7 @@ RTL87XX_BOARD_PINS = {
|
||||||
"RX2": 29,
|
"RX2": 29,
|
||||||
"SCK0": 18,
|
"SCK0": 18,
|
||||||
"SCK1": 18,
|
"SCK1": 18,
|
||||||
"SCL0": 22,
|
|
||||||
"SCL1": 18,
|
"SCL1": 18,
|
||||||
"SDA0": 19,
|
|
||||||
"SDA1": 23,
|
"SDA1": 23,
|
||||||
"TX0": 23,
|
"TX0": 23,
|
||||||
"TX2": 30,
|
"TX2": 30,
|
||||||
|
@ -1359,7 +1374,6 @@ RTL87XX_BOARD_PINS = {
|
||||||
"PA23": 23,
|
"PA23": 23,
|
||||||
"PA29": 29,
|
"PA29": 29,
|
||||||
"PA30": 30,
|
"PA30": 30,
|
||||||
"PWM0": 23,
|
|
||||||
"PWM1": 15,
|
"PWM1": 15,
|
||||||
"PWM2": 0,
|
"PWM2": 0,
|
||||||
"PWM3": 12,
|
"PWM3": 12,
|
||||||
|
|
|
@ -8,6 +8,13 @@ from esphome.const import (
|
||||||
CONF_OPTIMISTIC,
|
CONF_OPTIMISTIC,
|
||||||
CONF_RESTORE_VALUE,
|
CONF_RESTORE_VALUE,
|
||||||
CONF_SET_ACTION,
|
CONF_SET_ACTION,
|
||||||
|
CONF_DAY,
|
||||||
|
CONF_HOUR,
|
||||||
|
CONF_MINUTE,
|
||||||
|
CONF_MONTH,
|
||||||
|
CONF_SECOND,
|
||||||
|
CONF_TYPE,
|
||||||
|
CONF_YEAR,
|
||||||
)
|
)
|
||||||
|
|
||||||
from esphome.core import coroutine_with_priority
|
from esphome.core import coroutine_with_priority
|
||||||
|
@ -20,6 +27,10 @@ TemplateDate = template_ns.class_(
|
||||||
"TemplateDate", datetime.DateEntity, cg.PollingComponent
|
"TemplateDate", datetime.DateEntity, cg.PollingComponent
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TemplateTime = template_ns.class_(
|
||||||
|
"TemplateTime", datetime.TimeEntity, cg.PollingComponent
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate(config):
|
def validate(config):
|
||||||
config = config.copy()
|
config = config.copy()
|
||||||
|
@ -60,6 +71,13 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Optional(CONF_INITIAL_VALUE): cv.date_time(allowed_time=False),
|
cv.Optional(CONF_INITIAL_VALUE): cv.date_time(allowed_time=False),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
"TIME": datetime.time_schema(TemplateTime)
|
||||||
|
.extend(_BASE_SCHEMA)
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_INITIAL_VALUE): cv.date_time(allowed_date=False),
|
||||||
|
}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
upper=True,
|
upper=True,
|
||||||
),
|
),
|
||||||
|
@ -82,7 +100,22 @@ async def to_code(config):
|
||||||
cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))
|
cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))
|
||||||
|
|
||||||
if initial_value := config.get(CONF_INITIAL_VALUE):
|
if initial_value := config.get(CONF_INITIAL_VALUE):
|
||||||
cg.add(var.set_initial_value(initial_value))
|
if config[CONF_TYPE] == "DATE":
|
||||||
|
date_struct = cg.StructInitializer(
|
||||||
|
cg.ESPTime,
|
||||||
|
("day_of_month", initial_value[CONF_DAY]),
|
||||||
|
("month", initial_value[CONF_MONTH]),
|
||||||
|
("year", initial_value[CONF_YEAR]),
|
||||||
|
)
|
||||||
|
cg.add(var.set_initial_value(date_struct))
|
||||||
|
elif config[CONF_TYPE] == "TIME":
|
||||||
|
time_struct = cg.StructInitializer(
|
||||||
|
cg.ESPTime,
|
||||||
|
("second", initial_value[CONF_SECOND]),
|
||||||
|
("minute", initial_value[CONF_MINUTE]),
|
||||||
|
("hour", initial_value[CONF_HOUR]),
|
||||||
|
)
|
||||||
|
cg.add(var.set_initial_value(time_struct))
|
||||||
|
|
||||||
if CONF_SET_ACTION in config:
|
if CONF_SET_ACTION in config:
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
|
|
111
esphome/components/template/datetime/template_time.cpp
Normal file
111
esphome/components/template/datetime/template_time.cpp
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
#include "template_time.h"
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace template_ {
|
||||||
|
|
||||||
|
static const char *const TAG = "template.time";
|
||||||
|
|
||||||
|
void TemplateTime::setup() {
|
||||||
|
if (this->f_.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ESPTime state{};
|
||||||
|
|
||||||
|
if (!this->restore_value_) {
|
||||||
|
state = this->initial_value_;
|
||||||
|
} else {
|
||||||
|
datetime::TimeEntityRestoreState temp;
|
||||||
|
this->pref_ =
|
||||||
|
global_preferences->make_preference<datetime::TimeEntityRestoreState>(194434060U ^ this->get_object_id_hash());
|
||||||
|
if (this->pref_.load(&temp)) {
|
||||||
|
temp.apply(this);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// set to inital value if loading from pref failed
|
||||||
|
state = this->initial_value_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->hour_ = state.hour;
|
||||||
|
this->minute_ = state.minute;
|
||||||
|
this->second_ = state.second;
|
||||||
|
this->publish_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TemplateTime::update() {
|
||||||
|
if (!this->f_.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto val = (*this->f_)();
|
||||||
|
if (!val.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
this->hour_ = val->hour;
|
||||||
|
this->minute_ = val->minute;
|
||||||
|
this->second_ = val->second;
|
||||||
|
this->publish_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TemplateTime::control(const datetime::TimeCall &call) {
|
||||||
|
bool has_hour = call.get_hour().has_value();
|
||||||
|
bool has_minute = call.get_minute().has_value();
|
||||||
|
bool has_second = call.get_second().has_value();
|
||||||
|
|
||||||
|
ESPTime value = {};
|
||||||
|
if (has_hour)
|
||||||
|
value.hour = *call.get_hour();
|
||||||
|
|
||||||
|
if (has_minute)
|
||||||
|
value.minute = *call.get_minute();
|
||||||
|
|
||||||
|
if (has_second)
|
||||||
|
value.second = *call.get_second();
|
||||||
|
|
||||||
|
this->set_trigger_->trigger(value);
|
||||||
|
|
||||||
|
if (this->optimistic_) {
|
||||||
|
if (has_hour)
|
||||||
|
this->hour_ = *call.get_hour();
|
||||||
|
if (has_minute)
|
||||||
|
this->minute_ = *call.get_minute();
|
||||||
|
if (has_second)
|
||||||
|
this->second_ = *call.get_second();
|
||||||
|
this->publish_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->restore_value_) {
|
||||||
|
datetime::TimeEntityRestoreState temp = {};
|
||||||
|
if (has_hour) {
|
||||||
|
temp.hour = *call.get_hour();
|
||||||
|
} else {
|
||||||
|
temp.hour = this->hour_;
|
||||||
|
}
|
||||||
|
if (has_minute) {
|
||||||
|
temp.minute = *call.get_minute();
|
||||||
|
} else {
|
||||||
|
temp.minute = this->minute_;
|
||||||
|
}
|
||||||
|
if (has_second) {
|
||||||
|
temp.second = *call.get_second();
|
||||||
|
} else {
|
||||||
|
temp.second = this->second_;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pref_.save(&temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TemplateTime::dump_config() {
|
||||||
|
LOG_DATETIME_TIME("", "Template Time", this);
|
||||||
|
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace template_
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_DATETIME_TIME
|
46
esphome/components/template/datetime/template_time.h
Normal file
46
esphome/components/template/datetime/template_time.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
|
||||||
|
#include "esphome/components/datetime/time_entity.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/preferences.h"
|
||||||
|
#include "esphome/core/time.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace template_ {
|
||||||
|
|
||||||
|
class TemplateTime : public datetime::TimeEntity, public PollingComponent {
|
||||||
|
public:
|
||||||
|
void set_template(std::function<optional<ESPTime>()> &&f) { this->f_ = f; }
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
void update() override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
|
Trigger<ESPTime> *get_set_trigger() const { return this->set_trigger_; }
|
||||||
|
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||||
|
|
||||||
|
void set_initial_value(ESPTime initial_value) { this->initial_value_ = initial_value; }
|
||||||
|
void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void control(const datetime::TimeCall &call) override;
|
||||||
|
|
||||||
|
bool optimistic_{false};
|
||||||
|
ESPTime initial_value_{};
|
||||||
|
bool restore_value_{false};
|
||||||
|
Trigger<ESPTime> *set_trigger_ = new Trigger<ESPTime>();
|
||||||
|
optional<std::function<optional<ESPTime>()>> f_;
|
||||||
|
|
||||||
|
ESPPreferenceObject pref_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace template_
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_DATETIME_TIME
|
|
@ -14,7 +14,6 @@ from esphome.const import (
|
||||||
CONF_LAT_PIN = "lat_pin"
|
CONF_LAT_PIN = "lat_pin"
|
||||||
CONF_OE_PIN = "oe_pin"
|
CONF_OE_PIN = "oe_pin"
|
||||||
|
|
||||||
AUTO_LOAD = ["output"]
|
|
||||||
CODEOWNERS = ["@rnauber"]
|
CODEOWNERS = ["@rnauber"]
|
||||||
|
|
||||||
tlc5947_ns = cg.esphome_ns.namespace("tlc5947")
|
tlc5947_ns = cg.esphome_ns.namespace("tlc5947")
|
||||||
|
|
|
@ -2,18 +2,19 @@ import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import output
|
from esphome.components import output
|
||||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||||
from . import TLC5947
|
from .. import TLC5947, tlc5947_ns
|
||||||
|
|
||||||
DEPENDENCIES = ["tlc5947"]
|
DEPENDENCIES = ["tlc5947"]
|
||||||
CODEOWNERS = ["@rnauber"]
|
|
||||||
|
|
||||||
Channel = TLC5947.class_("Channel", output.FloatOutput)
|
TLC5947Channel = tlc5947_ns.class_(
|
||||||
|
"TLC5947Channel", output.FloatOutput, cg.Parented.template(TLC5947)
|
||||||
|
)
|
||||||
|
|
||||||
CONF_TLC5947_ID = "tlc5947_id"
|
CONF_TLC5947_ID = "tlc5947_id"
|
||||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TLC5947_ID): cv.use_id(TLC5947),
|
cv.GenerateID(CONF_TLC5947_ID): cv.use_id(TLC5947),
|
||||||
cv.Required(CONF_ID): cv.declare_id(Channel),
|
cv.Required(CONF_ID): cv.declare_id(TLC5947Channel),
|
||||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=65535),
|
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=65535),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
@ -22,7 +23,5 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await output.register_output(var, config)
|
await output.register_output(var, config)
|
||||||
|
await cg.register_parented(var, config[CONF_TLC5947_ID])
|
||||||
parent = await cg.get_variable(config[CONF_TLC5947_ID])
|
|
||||||
cg.add(var.set_parent(parent))
|
|
||||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
12
esphome/components/tlc5947/output/tlc5947_output.cpp
Normal file
12
esphome/components/tlc5947/output/tlc5947_output.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "tlc5947_output.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace tlc5947 {
|
||||||
|
|
||||||
|
void TLC5947Channel::write_state(float state) {
|
||||||
|
auto amount = static_cast<uint16_t>(state * 0xfff);
|
||||||
|
this->parent_->set_channel_value(this->channel_, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tlc5947
|
||||||
|
} // namespace esphome
|
22
esphome/components/tlc5947/output/tlc5947_output.h
Normal file
22
esphome/components/tlc5947/output/tlc5947_output.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
#include "esphome/components/output/float_output.h"
|
||||||
|
|
||||||
|
#include "../tlc5947.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace tlc5947 {
|
||||||
|
|
||||||
|
class TLC5947Channel : public output::FloatOutput, public Parented<TLC5947> {
|
||||||
|
public:
|
||||||
|
void set_channel(uint8_t channel) { this->channel_ = channel; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(float state) override;
|
||||||
|
uint8_t channel_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tlc5947
|
||||||
|
} // namespace esphome
|
|
@ -60,5 +60,14 @@ void TLC5947::loop() {
|
||||||
this->update_ = false;
|
this->update_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TLC5947::set_channel_value(uint16_t channel, uint16_t value) {
|
||||||
|
if (channel >= this->num_chips_ * N_CHANNELS_PER_CHIP)
|
||||||
|
return;
|
||||||
|
if (this->pwm_amounts_[channel] != value) {
|
||||||
|
this->update_ = true;
|
||||||
|
}
|
||||||
|
this->pwm_amounts_[channel] = value;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace tlc5947
|
} // namespace tlc5947
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -2,18 +2,16 @@
|
||||||
// TLC5947 24-Channel, 12-Bit PWM LED Driver
|
// TLC5947 24-Channel, 12-Bit PWM LED Driver
|
||||||
// https://www.ti.com/lit/ds/symlink/tlc5947.pdf
|
// https://www.ti.com/lit/ds/symlink/tlc5947.pdf
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "esphome/components/output/float_output.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/components/output/float_output.h"
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace tlc5947 {
|
namespace tlc5947 {
|
||||||
|
|
||||||
class TLC5947 : public Component {
|
class TLC5947 : public Component {
|
||||||
public:
|
public:
|
||||||
class Channel;
|
|
||||||
|
|
||||||
const uint8_t N_CHANNELS_PER_CHIP = 24;
|
const uint8_t N_CHANNELS_PER_CHIP = 24;
|
||||||
|
|
||||||
void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; }
|
void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; }
|
||||||
|
@ -31,31 +29,9 @@ class TLC5947 : public Component {
|
||||||
/// Send new values if they were updated.
|
/// Send new values if they were updated.
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
class Channel : public output::FloatOutput {
|
void set_channel_value(uint16_t channel, uint16_t value);
|
||||||
public:
|
|
||||||
void set_parent(TLC5947 *parent) { parent_ = parent; }
|
|
||||||
void set_channel(uint8_t channel) { channel_ = channel; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void write_state(float state) override {
|
|
||||||
auto amount = static_cast<uint16_t>(state * 0xfff);
|
|
||||||
this->parent_->set_channel_value_(this->channel_, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
TLC5947 *parent_;
|
|
||||||
uint8_t channel_;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void set_channel_value_(uint16_t channel, uint16_t value) {
|
|
||||||
if (channel >= this->num_chips_ * N_CHANNELS_PER_CHIP)
|
|
||||||
return;
|
|
||||||
if (this->pwm_amounts_[channel] != value) {
|
|
||||||
this->update_ = true;
|
|
||||||
}
|
|
||||||
this->pwm_amounts_[channel] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
GPIOPin *data_pin_;
|
GPIOPin *data_pin_;
|
||||||
GPIOPin *clock_pin_;
|
GPIOPin *clock_pin_;
|
||||||
GPIOPin *lat_pin_;
|
GPIOPin *lat_pin_;
|
||||||
|
|
|
@ -30,37 +30,37 @@ CONFIG_SCHEMA = cv.All(
|
||||||
|
|
||||||
|
|
||||||
def determine_config_register(polling_period):
|
def determine_config_register(polling_period):
|
||||||
if polling_period >= 16.0:
|
if polling_period >= 16000:
|
||||||
# 64 averaged conversions, max conversion time
|
# 64 averaged conversions, max conversion time
|
||||||
# 0000 00 111 11 00000
|
# 0000 00 111 11 00000
|
||||||
# 0000 0011 1110 0000
|
# 0000 0011 1110 0000
|
||||||
return 0x03E0
|
return 0x03E0
|
||||||
if polling_period >= 8.0:
|
if polling_period >= 8000:
|
||||||
# 64 averaged conversions, high conversion time
|
# 64 averaged conversions, high conversion time
|
||||||
# 0000 00 110 11 00000
|
# 0000 00 110 11 00000
|
||||||
# 0000 0011 0110 0000
|
# 0000 0011 0110 0000
|
||||||
return 0x0360
|
return 0x0360
|
||||||
if polling_period >= 4.0:
|
if polling_period >= 4000:
|
||||||
# 64 averaged conversions, mid conversion time
|
# 64 averaged conversions, mid conversion time
|
||||||
# 0000 00 101 11 00000
|
# 0000 00 101 11 00000
|
||||||
# 0000 0010 1110 0000
|
# 0000 0010 1110 0000
|
||||||
return 0x02E0
|
return 0x02E0
|
||||||
if polling_period >= 1.0:
|
if polling_period >= 1000:
|
||||||
# 64 averaged conversions, min conversion time
|
# 64 averaged conversions, min conversion time
|
||||||
# 0000 00 000 11 00000
|
# 0000 00 000 11 00000
|
||||||
# 0000 0000 0110 0000
|
# 0000 0000 0110 0000
|
||||||
return 0x0060
|
return 0x0060
|
||||||
if polling_period >= 0.5:
|
if polling_period >= 500:
|
||||||
# 32 averaged conversions, min conversion time
|
# 32 averaged conversions, min conversion time
|
||||||
# 0000 00 000 10 00000
|
# 0000 00 000 10 00000
|
||||||
# 0000 0000 0100 0000
|
# 0000 0000 0100 0000
|
||||||
return 0x0040
|
return 0x0040
|
||||||
if polling_period >= 0.25:
|
if polling_period >= 250:
|
||||||
# 8 averaged conversions, mid conversion time
|
# 8 averaged conversions, mid conversion time
|
||||||
# 0000 00 010 01 00000
|
# 0000 00 010 01 00000
|
||||||
# 0000 0001 0010 0000
|
# 0000 0001 0010 0000
|
||||||
return 0x0120
|
return 0x0120
|
||||||
if polling_period >= 0.125:
|
if polling_period >= 125:
|
||||||
# 8 averaged conversions, min conversion time
|
# 8 averaged conversions, min conversion time
|
||||||
# 0000 00 000 01 00000
|
# 0000 00 000 01 00000
|
||||||
# 0000 0000 0010 0000
|
# 0000 0000 0010 0000
|
||||||
|
@ -76,5 +76,5 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
update_period = config[CONF_UPDATE_INTERVAL].total_seconds
|
update_period = config[CONF_UPDATE_INTERVAL].total_milliseconds
|
||||||
cg.add(var.set_config(determine_config_register(update_period)))
|
cg.add(var.set_config(determine_config_register(update_period)))
|
||||||
|
|
|
@ -46,6 +46,14 @@ LibreTinyUARTComponent = uart_ns.class_(
|
||||||
"LibreTinyUARTComponent", UARTComponent, cg.Component
|
"LibreTinyUARTComponent", UARTComponent, cg.Component
|
||||||
)
|
)
|
||||||
|
|
||||||
|
NATIVE_UART_CLASSES = (
|
||||||
|
str(IDFUARTComponent),
|
||||||
|
str(ESP32ArduinoUARTComponent),
|
||||||
|
str(ESP8266UartComponent),
|
||||||
|
str(RP2040UartComponent),
|
||||||
|
str(LibreTinyUARTComponent),
|
||||||
|
)
|
||||||
|
|
||||||
UARTDevice = uart_ns.class_("UARTDevice")
|
UARTDevice = uart_ns.class_("UARTDevice")
|
||||||
UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action)
|
UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action)
|
||||||
UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action)
|
UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action)
|
||||||
|
@ -299,17 +307,18 @@ def final_validate_device_schema(
|
||||||
def validate_hub(hub_config):
|
def validate_hub(hub_config):
|
||||||
hub_schema = {}
|
hub_schema = {}
|
||||||
uart_id = hub_config[CONF_ID]
|
uart_id = hub_config[CONF_ID]
|
||||||
|
uart_id_type_str = str(uart_id.type)
|
||||||
devices = fv.full_config.get().data.setdefault(KEY_UART_DEVICES, {})
|
devices = fv.full_config.get().data.setdefault(KEY_UART_DEVICES, {})
|
||||||
device = devices.setdefault(uart_id, {})
|
device = devices.setdefault(uart_id, {})
|
||||||
|
|
||||||
if require_tx:
|
if require_tx and uart_id_type_str in NATIVE_UART_CLASSES:
|
||||||
hub_schema[
|
hub_schema[
|
||||||
cv.Required(
|
cv.Required(
|
||||||
CONF_TX_PIN,
|
CONF_TX_PIN,
|
||||||
msg=f"Component {name} requires this uart bus to declare a tx_pin",
|
msg=f"Component {name} requires this uart bus to declare a tx_pin",
|
||||||
)
|
)
|
||||||
] = validate_pin(CONF_TX_PIN, device)
|
] = validate_pin(CONF_TX_PIN, device)
|
||||||
if require_rx:
|
if require_rx and uart_id_type_str in NATIVE_UART_CLASSES:
|
||||||
hub_schema[
|
hub_schema[
|
||||||
cv.Required(
|
cv.Required(
|
||||||
CONF_RX_PIN,
|
CONF_RX_PIN,
|
||||||
|
|
|
@ -24,28 +24,24 @@ static const size_t SPEAKER_BUFFER_SIZE = 16 * RECEIVE_SIZE;
|
||||||
|
|
||||||
float VoiceAssistant::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }
|
float VoiceAssistant::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }
|
||||||
|
|
||||||
void VoiceAssistant::setup() {
|
bool VoiceAssistant::start_udp_socket_() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up Voice Assistant...");
|
|
||||||
|
|
||||||
global_voice_assistant = this;
|
|
||||||
|
|
||||||
this->socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
this->socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||||
if (socket_ == nullptr) {
|
if (this->socket_ == nullptr) {
|
||||||
ESP_LOGW(TAG, "Could not create socket");
|
ESP_LOGE(TAG, "Could not create socket");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
int enable = 1;
|
int enable = 1;
|
||||||
int err = socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
int err = this->socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
|
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
|
||||||
// we can still continue
|
// we can still continue
|
||||||
}
|
}
|
||||||
err = socket_->setblocking(false);
|
err = this->socket_->setblocking(false);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
|
ESP_LOGE(TAG, "Socket unable to set nonblocking mode: errno %d", err);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_SPEAKER
|
#ifdef USE_SPEAKER
|
||||||
|
@ -54,18 +50,30 @@ void VoiceAssistant::setup() {
|
||||||
|
|
||||||
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), 6055);
|
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), 6055);
|
||||||
if (sl == 0) {
|
if (sl == 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
|
ESP_LOGE(TAG, "Socket unable to set sockaddr: errno %d", errno);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = socket_->bind((struct sockaddr *) &server, sizeof(server));
|
err = this->socket_->bind((struct sockaddr *) &server, sizeof(server));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
|
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
this->udp_socket_running_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoiceAssistant::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up Voice Assistant...");
|
||||||
|
|
||||||
|
global_voice_assistant = this;
|
||||||
|
|
||||||
|
#ifdef USE_SPEAKER
|
||||||
|
if (this->speaker_ != nullptr) {
|
||||||
ExternalRAMAllocator<uint8_t> speaker_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
ExternalRAMAllocator<uint8_t> speaker_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||||
this->speaker_buffer_ = speaker_allocator.allocate(SPEAKER_BUFFER_SIZE);
|
this->speaker_buffer_ = speaker_allocator.allocate(SPEAKER_BUFFER_SIZE);
|
||||||
if (this->speaker_buffer_ == nullptr) {
|
if (this->speaker_buffer_ == nullptr) {
|
||||||
|
@ -238,8 +246,20 @@ void VoiceAssistant::loop() {
|
||||||
size_t available = this->ring_buffer_->available();
|
size_t available = this->ring_buffer_->available();
|
||||||
while (available >= SEND_BUFFER_SIZE) {
|
while (available >= SEND_BUFFER_SIZE) {
|
||||||
size_t read_bytes = this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0);
|
size_t read_bytes = this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0);
|
||||||
this->socket_->sendto(this->send_buffer_, read_bytes, 0, (struct sockaddr *) &this->dest_addr_,
|
if (this->audio_mode_ == AUDIO_MODE_API) {
|
||||||
sizeof(this->dest_addr_));
|
api::VoiceAssistantAudio msg;
|
||||||
|
msg.data.assign((char *) this->send_buffer_, read_bytes);
|
||||||
|
this->api_client_->send_voice_assistant_audio(msg);
|
||||||
|
} else {
|
||||||
|
if (!this->udp_socket_running_) {
|
||||||
|
if (!this->start_udp_socket_()) {
|
||||||
|
this->set_state_(State::STOP_MICROPHONE, State::IDLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->socket_->sendto(this->send_buffer_, read_bytes, 0, (struct sockaddr *) &this->dest_addr_,
|
||||||
|
sizeof(this->dest_addr_));
|
||||||
|
}
|
||||||
available = this->ring_buffer_->available();
|
available = this->ring_buffer_->available();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,22 +288,25 @@ void VoiceAssistant::loop() {
|
||||||
#ifdef USE_SPEAKER
|
#ifdef USE_SPEAKER
|
||||||
if (this->speaker_ != nullptr) {
|
if (this->speaker_ != nullptr) {
|
||||||
ssize_t received_len = 0;
|
ssize_t received_len = 0;
|
||||||
if (this->speaker_buffer_index_ + RECEIVE_SIZE < SPEAKER_BUFFER_SIZE) {
|
if (this->audio_mode_ == AUDIO_MODE_UDP) {
|
||||||
received_len = this->socket_->read(this->speaker_buffer_ + this->speaker_buffer_index_, RECEIVE_SIZE);
|
if (this->speaker_buffer_index_ + RECEIVE_SIZE < SPEAKER_BUFFER_SIZE) {
|
||||||
if (received_len > 0) {
|
received_len = this->socket_->read(this->speaker_buffer_ + this->speaker_buffer_index_, RECEIVE_SIZE);
|
||||||
this->speaker_buffer_index_ += received_len;
|
if (received_len > 0) {
|
||||||
this->speaker_buffer_size_ += received_len;
|
this->speaker_buffer_index_ += received_len;
|
||||||
this->speaker_bytes_received_ += received_len;
|
this->speaker_buffer_size_ += received_len;
|
||||||
|
this->speaker_bytes_received_ += received_len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "Receive buffer full");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ESP_LOGD(TAG, "Receive buffer full");
|
|
||||||
}
|
}
|
||||||
// Build a small buffer of audio before sending to the speaker
|
// Build a small buffer of audio before sending to the speaker
|
||||||
if (this->speaker_bytes_received_ > RECEIVE_SIZE * 4)
|
bool end_of_stream = this->stream_ended_ && (this->audio_mode_ == AUDIO_MODE_API || received_len < 0);
|
||||||
|
if (this->speaker_bytes_received_ > RECEIVE_SIZE * 4 || end_of_stream)
|
||||||
this->write_speaker_();
|
this->write_speaker_();
|
||||||
if (this->wait_for_stream_end_) {
|
if (this->wait_for_stream_end_) {
|
||||||
this->cancel_timeout("playing");
|
this->cancel_timeout("playing");
|
||||||
if (this->stream_ended_ && received_len < 0) {
|
if (end_of_stream) {
|
||||||
ESP_LOGD(TAG, "End of audio stream received");
|
ESP_LOGD(TAG, "End of audio stream received");
|
||||||
this->cancel_timeout("speaker-timeout");
|
this->cancel_timeout("speaker-timeout");
|
||||||
this->set_state_(State::RESPONSE_FINISHED, State::RESPONSE_FINISHED);
|
this->set_state_(State::RESPONSE_FINISHED, State::RESPONSE_FINISHED);
|
||||||
|
@ -428,6 +451,22 @@ void VoiceAssistant::failed_to_start() {
|
||||||
this->set_state_(State::STOP_MICROPHONE, State::IDLE);
|
this->set_state_(State::STOP_MICROPHONE, State::IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VoiceAssistant::start_streaming() {
|
||||||
|
if (this->state_ != State::STARTING_PIPELINE) {
|
||||||
|
this->signal_stop_();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Client started, streaming microphone");
|
||||||
|
this->audio_mode_ = AUDIO_MODE_API;
|
||||||
|
|
||||||
|
if (this->mic_->is_running()) {
|
||||||
|
this->set_state_(State::STREAMING_MICROPHONE, State::STREAMING_MICROPHONE);
|
||||||
|
} else {
|
||||||
|
this->set_state_(State::START_MICROPHONE, State::STREAMING_MICROPHONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VoiceAssistant::start_streaming(struct sockaddr_storage *addr, uint16_t port) {
|
void VoiceAssistant::start_streaming(struct sockaddr_storage *addr, uint16_t port) {
|
||||||
if (this->state_ != State::STARTING_PIPELINE) {
|
if (this->state_ != State::STARTING_PIPELINE) {
|
||||||
this->signal_stop_();
|
this->signal_stop_();
|
||||||
|
@ -435,6 +474,7 @@ void VoiceAssistant::start_streaming(struct sockaddr_storage *addr, uint16_t por
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Client started, streaming microphone");
|
ESP_LOGD(TAG, "Client started, streaming microphone");
|
||||||
|
this->audio_mode_ = AUDIO_MODE_UDP;
|
||||||
|
|
||||||
memcpy(&this->dest_addr_, addr, sizeof(this->dest_addr_));
|
memcpy(&this->dest_addr_, addr, sizeof(this->dest_addr_));
|
||||||
if (this->dest_addr_.ss_family == AF_INET) {
|
if (this->dest_addr_.ss_family == AF_INET) {
|
||||||
|
@ -688,6 +728,17 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) {
|
||||||
|
if (this->speaker_buffer_index_ + msg.data.length() < SPEAKER_BUFFER_SIZE) {
|
||||||
|
memcpy(this->speaker_buffer_ + this->speaker_buffer_index_, msg.data.data(), msg.data.length());
|
||||||
|
this->speaker_buffer_index_ += msg.data.length();
|
||||||
|
this->speaker_buffer_size_ += msg.data.length();
|
||||||
|
this->speaker_bytes_received_ += msg.data.length();
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Cannot receive audio, buffer is full");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
} // namespace voice_assistant
|
} // namespace voice_assistant
|
||||||
|
|
|
@ -29,9 +29,14 @@ namespace voice_assistant {
|
||||||
|
|
||||||
// Version 1: Initial version
|
// Version 1: Initial version
|
||||||
// Version 2: Adds raw speaker support
|
// Version 2: Adds raw speaker support
|
||||||
// Version 3: Unused/skip
|
static const uint32_t LEGACY_INITIAL_VERSION = 1;
|
||||||
static const uint32_t INITIAL_VERSION = 1;
|
static const uint32_t LEGACY_SPEAKER_SUPPORT = 2;
|
||||||
static const uint32_t SPEAKER_SUPPORT = 2;
|
|
||||||
|
enum VoiceAssistantFeature : uint32_t {
|
||||||
|
FEATURE_VOICE_ASSISTANT = 1 << 0,
|
||||||
|
FEATURE_SPEAKER = 1 << 1,
|
||||||
|
FEATURE_API_AUDIO = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
enum class State {
|
enum class State {
|
||||||
IDLE,
|
IDLE,
|
||||||
|
@ -49,11 +54,17 @@ enum class State {
|
||||||
RESPONSE_FINISHED,
|
RESPONSE_FINISHED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum AudioMode : uint8_t {
|
||||||
|
AUDIO_MODE_UDP,
|
||||||
|
AUDIO_MODE_API,
|
||||||
|
};
|
||||||
|
|
||||||
class VoiceAssistant : public Component {
|
class VoiceAssistant : public Component {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
void start_streaming();
|
||||||
void start_streaming(struct sockaddr_storage *addr, uint16_t port);
|
void start_streaming(struct sockaddr_storage *addr, uint16_t port);
|
||||||
void failed_to_start();
|
void failed_to_start();
|
||||||
|
|
||||||
|
@ -71,19 +82,32 @@ class VoiceAssistant : public Component {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t get_version() const {
|
uint32_t get_legacy_version() const {
|
||||||
#ifdef USE_SPEAKER
|
#ifdef USE_SPEAKER
|
||||||
if (this->speaker_ != nullptr) {
|
if (this->speaker_ != nullptr) {
|
||||||
return SPEAKER_SUPPORT;
|
return LEGACY_SPEAKER_SUPPORT;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return INITIAL_VERSION;
|
return LEGACY_INITIAL_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_feature_flags() const {
|
||||||
|
uint32_t flags = 0;
|
||||||
|
flags |= VoiceAssistantFeature::FEATURE_VOICE_ASSISTANT;
|
||||||
|
#ifdef USE_SPEAKER
|
||||||
|
if (this->speaker_ != nullptr) {
|
||||||
|
flags |= VoiceAssistantFeature::FEATURE_SPEAKER;
|
||||||
|
flags |= VoiceAssistantFeature::FEATURE_API_AUDIO;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
void request_start(bool continuous, bool silence_detection);
|
void request_start(bool continuous, bool silence_detection);
|
||||||
void request_stop();
|
void request_stop();
|
||||||
|
|
||||||
void on_event(const api::VoiceAssistantEventResponse &msg);
|
void on_event(const api::VoiceAssistantEventResponse &msg);
|
||||||
|
void on_audio(const api::VoiceAssistantAudio &msg);
|
||||||
|
|
||||||
bool is_running() const { return this->state_ != State::IDLE; }
|
bool is_running() const { return this->state_ != State::IDLE; }
|
||||||
void set_continuous(bool continuous) { this->continuous_ = continuous; }
|
void set_continuous(bool continuous) { this->continuous_ = continuous; }
|
||||||
|
@ -201,6 +225,10 @@ class VoiceAssistant : public Component {
|
||||||
|
|
||||||
State state_{State::IDLE};
|
State state_{State::IDLE};
|
||||||
State desired_state_{State::IDLE};
|
State desired_state_{State::IDLE};
|
||||||
|
|
||||||
|
AudioMode audio_mode_{AUDIO_MODE_UDP};
|
||||||
|
bool udp_socket_running_{false};
|
||||||
|
bool start_udp_socket_();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class StartAction : public Action<Ts...>, public Parented<VoiceAssistant> {
|
template<typename... Ts> class StartAction : public Action<Ts...>, public Parented<VoiceAssistant> {
|
||||||
|
|
|
@ -12,6 +12,8 @@ ListEntitiesIterator::ListEntitiesIterator(WebServer *web_server) : web_server_(
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(
|
this->web_server_->events_.send(
|
||||||
this->web_server_->binary_sensor_json(binary_sensor, binary_sensor->state, DETAIL_ALL).c_str(), "state");
|
this->web_server_->binary_sensor_json(binary_sensor, binary_sensor->state, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
|
@ -19,30 +21,40 @@ bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
bool ListEntitiesIterator::on_cover(cover::Cover *cover) {
|
bool ListEntitiesIterator::on_cover(cover::Cover *cover) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->cover_json(cover, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->cover_json(cover, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool ListEntitiesIterator::on_fan(fan::Fan *fan) {
|
bool ListEntitiesIterator::on_fan(fan::Fan *fan) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->fan_json(fan, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->fan_json(fan, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool ListEntitiesIterator::on_light(light::LightState *light) {
|
bool ListEntitiesIterator::on_light(light::LightState *light) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->light_json(light, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->light_json(light, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) {
|
bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->sensor_json(sensor, sensor->state, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->sensor_json(sensor, sensor->state, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) {
|
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->switch_json(a_switch, a_switch->state, DETAIL_ALL).c_str(),
|
this->web_server_->events_.send(this->web_server_->switch_json(a_switch, a_switch->state, DETAIL_ALL).c_str(),
|
||||||
"state");
|
"state");
|
||||||
return true;
|
return true;
|
||||||
|
@ -50,12 +62,16 @@ bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) {
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
bool ListEntitiesIterator::on_button(button::Button *button) {
|
bool ListEntitiesIterator::on_button(button::Button *button) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->button_json(button, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->button_json(button, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(
|
this->web_server_->events_.send(
|
||||||
this->web_server_->text_sensor_json(text_sensor, text_sensor->state, DETAIL_ALL).c_str(), "state");
|
this->web_server_->text_sensor_json(text_sensor, text_sensor->state, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
|
@ -63,6 +79,8 @@ bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) {
|
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->lock_json(a_lock, a_lock->state, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->lock_json(a_lock, a_lock->state, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +88,8 @@ bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) {
|
||||||
|
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
bool ListEntitiesIterator::on_climate(climate::Climate *climate) {
|
bool ListEntitiesIterator::on_climate(climate::Climate *climate) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->climate_json(climate, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->climate_json(climate, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -77,6 +97,8 @@ bool ListEntitiesIterator::on_climate(climate::Climate *climate) {
|
||||||
|
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool ListEntitiesIterator::on_number(number::Number *number) {
|
bool ListEntitiesIterator::on_number(number::Number *number) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->number_json(number, number->state, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->number_json(number, number->state, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -84,13 +106,24 @@ bool ListEntitiesIterator::on_number(number::Number *number) {
|
||||||
|
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) {
|
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->date_json(date, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->date_json(date, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) {
|
||||||
|
this->web_server_->events_.send(this->web_server_->time_json(time, DETAIL_ALL).c_str(), "state");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool ListEntitiesIterator::on_text(text::Text *text) {
|
bool ListEntitiesIterator::on_text(text::Text *text) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->text_json(text, text->state, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->text_json(text, text->state, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -98,6 +131,8 @@ bool ListEntitiesIterator::on_text(text::Text *text) {
|
||||||
|
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
bool ListEntitiesIterator::on_select(select::Select *select) {
|
bool ListEntitiesIterator::on_select(select::Select *select) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(this->web_server_->select_json(select, select->state, DETAIL_ALL).c_str(), "state");
|
this->web_server_->events_.send(this->web_server_->select_json(select, select->state, DETAIL_ALL).c_str(), "state");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -105,6 +140,8 @@ bool ListEntitiesIterator::on_select(select::Select *select) {
|
||||||
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||||
|
if (this->web_server_->events_.count() == 0)
|
||||||
|
return true;
|
||||||
this->web_server_->events_.send(
|
this->web_server_->events_.send(
|
||||||
this->web_server_->alarm_control_panel_json(a_alarm_control_panel, a_alarm_control_panel->get_state(), DETAIL_ALL)
|
this->web_server_->alarm_control_panel_json(a_alarm_control_panel, a_alarm_control_panel->get_state(), DETAIL_ALL)
|
||||||
.c_str(),
|
.c_str(),
|
||||||
|
|
|
@ -44,6 +44,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool on_date(datetime::DateEntity *date) override;
|
bool on_date(datetime::DateEntity *date) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
bool on_time(datetime::TimeEntity *time) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool on_text(text::Text *text) override;
|
bool on_text(text::Text *text) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -416,6 +416,8 @@ void WebServer::handle_js_request(AsyncWebServerRequest *request) {
|
||||||
|
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
void WebServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
void WebServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
|
@ -449,6 +451,8 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail
|
||||||
|
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
|
void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->text_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->text_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
|
@ -471,6 +475,8 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std:
|
||||||
|
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
void WebServer::on_switch_update(switch_::Switch *obj, bool state) {
|
void WebServer::on_switch_update(switch_::Switch *obj, bool state) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->switch_json(obj, state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->switch_json(obj, state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
|
std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
|
||||||
|
@ -532,6 +538,8 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
|
void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->binary_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->binary_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
|
std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
|
||||||
|
@ -553,7 +561,11 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
void WebServer::on_fan_update(fan::Fan *obj) { this->events_.send(this->fan_json(obj, DETAIL_STATE).c_str(), "state"); }
|
void WebServer::on_fan_update(fan::Fan *obj) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
|
this->events_.send(this->fan_json(obj, DETAIL_STATE).c_str(), "state");
|
||||||
|
}
|
||||||
std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
|
std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
|
||||||
return json::build_json([obj, start_config](JsonObject root) {
|
return json::build_json([obj, start_config](JsonObject root) {
|
||||||
set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
|
set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
|
||||||
|
@ -623,6 +635,8 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
|
||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
void WebServer::on_light_update(light::LightState *obj) {
|
void WebServer::on_light_update(light::LightState *obj) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->light_json(obj, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->light_json(obj, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
|
@ -729,6 +743,8 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi
|
||||||
|
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
void WebServer::on_cover_update(cover::Cover *obj) {
|
void WebServer::on_cover_update(cover::Cover *obj) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->cover_json(obj, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->cover_json(obj, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
|
@ -798,6 +814,8 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
|
||||||
|
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
void WebServer::on_number_update(number::Number *obj, float state) {
|
void WebServer::on_number_update(number::Number *obj, float state) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->number_json(obj, state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->number_json(obj, state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
|
@ -856,6 +874,8 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
|
||||||
|
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
void WebServer::on_date_update(datetime::DateEntity *obj) {
|
void WebServer::on_date_update(datetime::DateEntity *obj) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->date_json(obj, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->date_json(obj, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
|
@ -894,15 +914,63 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
|
||||||
std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) {
|
std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) {
|
||||||
return json::build_json([obj, start_config](JsonObject root) {
|
return json::build_json([obj, start_config](JsonObject root) {
|
||||||
set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
|
||||||
std::string value = str_sprintf("%d-%d-%d", obj->year, obj->month, obj->day);
|
std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
|
||||||
root["value"] = value;
|
root["value"] = value;
|
||||||
root["state"] = value;
|
root["state"] = value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif // USE_DATETIME_DATE
|
#endif // USE_DATETIME_DATE
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void WebServer::on_time_update(datetime::TimeEntity *obj) {
|
||||||
|
this->events_.send(this->time_json(obj, DETAIL_STATE).c_str(), "state");
|
||||||
|
}
|
||||||
|
void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
|
for (auto *obj : App.get_times()) {
|
||||||
|
if (obj->get_object_id() != match.id)
|
||||||
|
continue;
|
||||||
|
if (request->method() == HTTP_GET && match.method.empty()) {
|
||||||
|
std::string data = this->time_json(obj, DETAIL_STATE);
|
||||||
|
request->send(200, "application/json", data.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (match.method != "set") {
|
||||||
|
request->send(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto call = obj->make_call();
|
||||||
|
|
||||||
|
if (!request->hasParam("value")) {
|
||||||
|
request->send(409);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request->hasParam("value")) {
|
||||||
|
std::string value = request->getParam("value")->value().c_str();
|
||||||
|
call.set_time(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->schedule_([call]() mutable { call.perform(); });
|
||||||
|
request->send(200);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
request->send(404);
|
||||||
|
}
|
||||||
|
std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) {
|
||||||
|
return json::build_json([obj, start_config](JsonObject root) {
|
||||||
|
set_json_id(root, obj, "time-" + obj->get_object_id(), start_config);
|
||||||
|
std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
|
||||||
|
root["value"] = value;
|
||||||
|
root["state"] = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif // USE_DATETIME_TIME
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
void WebServer::on_text_update(text::Text *obj, const std::string &state) {
|
void WebServer::on_text_update(text::Text *obj, const std::string &state) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->text_json(obj, state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->text_json(obj, state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
|
@ -954,6 +1022,8 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json
|
||||||
|
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
void WebServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
|
void WebServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->select_json(obj, state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->select_json(obj, state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
|
@ -1008,6 +1078,8 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value
|
||||||
|
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
void WebServer::on_climate_update(climate::Climate *obj) {
|
void WebServer::on_climate_update(climate::Climate *obj) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->climate_json(obj, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->climate_json(obj, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1149,6 +1221,8 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
||||||
|
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
void WebServer::on_lock_update(lock::Lock *obj) {
|
void WebServer::on_lock_update(lock::Lock *obj) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->lock_json(obj, obj->state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->lock_json(obj, obj->state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
|
std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
|
||||||
|
@ -1185,6 +1259,8 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat
|
||||||
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
|
void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
|
||||||
|
if (this->events_.count() == 0)
|
||||||
|
return;
|
||||||
this->events_.send(this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj,
|
std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj,
|
||||||
|
@ -1290,6 +1366,11 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "time")
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "text")
|
if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "text")
|
||||||
return true;
|
return true;
|
||||||
|
@ -1415,6 +1496,13 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
if (match.domain == "time") {
|
||||||
|
this->handle_time_request(request, match);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
if (match.domain == "text") {
|
if (match.domain == "text") {
|
||||||
this->handle_text_request(request, match);
|
this->handle_text_request(request, match);
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
#include <deque>
|
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/semphr.h>
|
#include <freertos/semphr.h>
|
||||||
|
#include <deque>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if USE_WEBSERVER_VERSION >= 2
|
#if USE_WEBSERVER_VERSION >= 2
|
||||||
|
@ -230,6 +230,15 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||||
std::string date_json(datetime::DateEntity *obj, JsonDetail start_config);
|
std::string date_json(datetime::DateEntity *obj, JsonDetail start_config);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void on_time_update(datetime::TimeEntity *obj) override;
|
||||||
|
/// Handle a time request under '/time/<id>'.
|
||||||
|
void handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||||
|
|
||||||
|
/// Dump the time state with its value as a JSON string.
|
||||||
|
std::string time_json(datetime::TimeEntity *obj, JsonDetail start_config);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
void on_text_update(text::Text *obj, const std::string &state) override;
|
void on_text_update(text::Text *obj, const std::string &state) override;
|
||||||
/// Handle a text input request under '/text/<id>'.
|
/// Handle a text input request under '/text/<id>'.
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
|
|
||||||
#include <esp_http_server.h>
|
#include <esp_http_server.h>
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace web_server_idf {
|
namespace web_server_idf {
|
||||||
|
@ -251,6 +251,8 @@ class AsyncEventSource : public AsyncWebHandler {
|
||||||
|
|
||||||
void send(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
|
void send(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
|
||||||
|
|
||||||
|
size_t count() const { return this->sessions_.size(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string url_;
|
std::string url_;
|
||||||
std::set<AsyncEventSourceResponse *> sessions_;
|
std::set<AsyncEventSourceResponse *> sessions_;
|
||||||
|
|
|
@ -582,14 +582,14 @@ void WiFiComponent::wifi_pre_setup_() {
|
||||||
}
|
}
|
||||||
WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() {
|
WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() {
|
||||||
auto status = WiFiClass::status();
|
auto status = WiFiClass::status();
|
||||||
if (status == WL_CONNECTED) {
|
if (status == WL_CONNECT_FAILED || status == WL_CONNECTION_LOST) {
|
||||||
return WiFiSTAConnectStatus::CONNECTED;
|
|
||||||
} else if (status == WL_CONNECT_FAILED || status == WL_CONNECTION_LOST) {
|
|
||||||
return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED;
|
return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED;
|
||||||
} else if (status == WL_NO_SSID_AVAIL) {
|
} else if (status == WL_NO_SSID_AVAIL) {
|
||||||
return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND;
|
return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND;
|
||||||
} else if (s_sta_connecting) {
|
} else if (s_sta_connecting) {
|
||||||
return WiFiSTAConnectStatus::CONNECTING;
|
return WiFiSTAConnectStatus::CONNECTING;
|
||||||
|
} else if (status == WL_CONNECTED) {
|
||||||
|
return WiFiSTAConnectStatus::CONNECTED;
|
||||||
}
|
}
|
||||||
return WiFiSTAConnectStatus::IDLE;
|
return WiFiSTAConnectStatus::IDLE;
|
||||||
}
|
}
|
||||||
|
@ -707,7 +707,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
|
||||||
*conf.ap.password = 0;
|
*conf.ap.password = 0;
|
||||||
} else {
|
} else {
|
||||||
conf.ap.authmode = WIFI_AUTH_WPA2_PSK;
|
conf.ap.authmode = WIFI_AUTH_WPA2_PSK;
|
||||||
strncpy(reinterpret_cast<char *>(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.ssid));
|
strncpy(reinterpret_cast<char *>(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.password));
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.ap.pairwise_cipher = WIFI_CIPHER_TYPE_CCMP;
|
conf.ap.pairwise_cipher = WIFI_CIPHER_TYPE_CCMP;
|
||||||
|
|
1
esphome/components/xiaomi_hhccjcy10/__init__.py
Normal file
1
esphome/components/xiaomi_hhccjcy10/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@fariouche"]
|
96
esphome/components/xiaomi_hhccjcy10/sensor.py
Normal file
96
esphome/components/xiaomi_hhccjcy10/sensor.py
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, esp32_ble_tracker
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_MAC_ADDRESS,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
ICON_WATER_PERCENT,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_MOISTURE,
|
||||||
|
CONF_ILLUMINANCE,
|
||||||
|
UNIT_LUX,
|
||||||
|
CONF_CONDUCTIVITY,
|
||||||
|
UNIT_MICROSIEMENS_PER_CENTIMETER,
|
||||||
|
ICON_FLOWER,
|
||||||
|
DEVICE_CLASS_BATTERY,
|
||||||
|
CONF_BATTERY_LEVEL,
|
||||||
|
)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||||
|
|
||||||
|
xiaomi_hhccjcy10_ns = cg.esphome_ns.namespace("xiaomi_hhccjcy10")
|
||||||
|
XiaomiHHCCJCY10 = xiaomi_hhccjcy10_ns.class_(
|
||||||
|
"XiaomiHHCCJCY10", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(XiaomiHHCCJCY10),
|
||||||
|
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||||
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_MOISTURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
icon=ICON_WATER_PERCENT,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_LUX,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_CONDUCTIVITY): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_MICROSIEMENS_PER_CENTIMETER,
|
||||||
|
icon=ICON_FLOWER,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_BATTERY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await esp32_ble_tracker.register_ble_device(var, config)
|
||||||
|
|
||||||
|
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||||
|
|
||||||
|
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||||
|
sens = await sensor.new_sensor(temperature_config)
|
||||||
|
cg.add(var.set_temperature(sens))
|
||||||
|
if moisture_config := config.get(CONF_MOISTURE):
|
||||||
|
sens = await sensor.new_sensor(moisture_config)
|
||||||
|
cg.add(var.set_moisture(sens))
|
||||||
|
if illuminance_config := config.get(CONF_ILLUMINANCE):
|
||||||
|
sens = await sensor.new_sensor(illuminance_config)
|
||||||
|
cg.add(var.set_illuminance(sens))
|
||||||
|
if conductivity_config := config.get(CONF_CONDUCTIVITY):
|
||||||
|
sens = await sensor.new_sensor(conductivity_config)
|
||||||
|
cg.add(var.set_conductivity(sens))
|
||||||
|
if battery_level_config := config.get(CONF_BATTERY_LEVEL):
|
||||||
|
sens = await sensor.new_sensor(battery_level_config)
|
||||||
|
cg.add(var.set_battery_level(sens))
|
68
esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp
Normal file
68
esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.cpp
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#include "xiaomi_hhccjcy10.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace xiaomi_hhccjcy10 {
|
||||||
|
|
||||||
|
static const char *const TAG = "xiaomi_hhccjcy10";
|
||||||
|
|
||||||
|
void XiaomiHHCCJCY10::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Xiaomi HHCCJCY10");
|
||||||
|
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||||
|
LOG_SENSOR(" ", "Moisture", this->moisture_);
|
||||||
|
LOG_SENSOR(" ", "Conductivity", this->conductivity_);
|
||||||
|
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
|
||||||
|
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XiaomiHHCCJCY10::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
|
if (device.address_uint64() != this->address_) {
|
||||||
|
ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str());
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
for (auto &service_data : device.get_service_datas()) {
|
||||||
|
if (!service_data.uuid.contains(0x50, 0xFD)) {
|
||||||
|
ESP_LOGVV(TAG, "no tuya service data UUID.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (service_data.data.size() != 9) { // tuya alternate between two service data
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const uint8_t *data = service_data.data.data();
|
||||||
|
|
||||||
|
if (this->temperature_ != nullptr) {
|
||||||
|
const int16_t temperature = encode_uint16(data[1], data[2]);
|
||||||
|
this->temperature_->publish_state((float) temperature / 10.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->moisture_ != nullptr)
|
||||||
|
this->moisture_->publish_state(data[0]);
|
||||||
|
|
||||||
|
if (this->conductivity_ != nullptr) {
|
||||||
|
const uint16_t conductivity = encode_uint16(data[7], data[8]);
|
||||||
|
this->conductivity_->publish_state((float) conductivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->illuminance_ != nullptr) {
|
||||||
|
const uint32_t illuminance = encode_uint24(data[3], data[4], data[5]);
|
||||||
|
this->illuminance_->publish_state((float) illuminance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->battery_level_ != nullptr)
|
||||||
|
this->battery_level_->publish_state(data[6]);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xiaomi_hhccjcy10
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
38
esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h
Normal file
38
esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace xiaomi_hhccjcy10 {
|
||||||
|
|
||||||
|
class XiaomiHHCCJCY10 : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
|
||||||
|
public:
|
||||||
|
void set_address(uint64_t address) { this->address_ = address; }
|
||||||
|
|
||||||
|
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
void set_temperature(sensor::Sensor *temperature) { this->temperature_ = temperature; }
|
||||||
|
void set_moisture(sensor::Sensor *moisture) { this->moisture_ = moisture; }
|
||||||
|
void set_conductivity(sensor::Sensor *conductivity) { this->conductivity_ = conductivity; }
|
||||||
|
void set_illuminance(sensor::Sensor *illuminance) { this->illuminance_ = illuminance; }
|
||||||
|
void set_battery_level(sensor::Sensor *battery_level) { this->battery_level_ = battery_level; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint64_t address_;
|
||||||
|
sensor::Sensor *temperature_{nullptr};
|
||||||
|
sensor::Sensor *moisture_{nullptr};
|
||||||
|
sensor::Sensor *conductivity_{nullptr};
|
||||||
|
sensor::Sensor *illuminance_{nullptr};
|
||||||
|
sensor::Sensor *battery_level_{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xiaomi_hhccjcy10
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
|
@ -676,6 +676,7 @@ CONF_REVERSED = "reversed"
|
||||||
CONF_RGB_ORDER = "rgb_order"
|
CONF_RGB_ORDER = "rgb_order"
|
||||||
CONF_RGBW = "rgbw"
|
CONF_RGBW = "rgbw"
|
||||||
CONF_RISING_EDGE = "rising_edge"
|
CONF_RISING_EDGE = "rising_edge"
|
||||||
|
CONF_RMT_CHANNEL = "rmt_channel"
|
||||||
CONF_ROTATION = "rotation"
|
CONF_ROTATION = "rotation"
|
||||||
CONF_RS_PIN = "rs_pin"
|
CONF_RS_PIN = "rs_pin"
|
||||||
CONF_RTD_NOMINAL_RESISTANCE = "rtd_nominal_resistance"
|
CONF_RTD_NOMINAL_RESISTANCE = "rtd_nominal_resistance"
|
||||||
|
|
|
@ -81,13 +81,11 @@ void Application::loop() {
|
||||||
|
|
||||||
const uint32_t now = millis();
|
const uint32_t now = millis();
|
||||||
|
|
||||||
if (HighFrequencyLoopRequester::is_high_frequency()) {
|
auto elapsed = now - this->last_loop_;
|
||||||
|
if (elapsed >= this->loop_interval_ || HighFrequencyLoopRequester::is_high_frequency()) {
|
||||||
yield();
|
yield();
|
||||||
} else {
|
} else {
|
||||||
uint32_t delay_time = this->loop_interval_;
|
uint32_t delay_time = this->loop_interval_ - elapsed;
|
||||||
if (now - this->last_loop_ < this->loop_interval_)
|
|
||||||
delay_time = this->loop_interval_ - (now - this->last_loop_);
|
|
||||||
|
|
||||||
uint32_t next_schedule = this->scheduler.next_schedule_in().value_or(delay_time);
|
uint32_t next_schedule = this->scheduler.next_schedule_in().value_or(delay_time);
|
||||||
// next_schedule is max 0.5*delay_time
|
// next_schedule is max 0.5*delay_time
|
||||||
// otherwise interval=0 schedules result in constant looping with almost no sleep
|
// otherwise interval=0 schedules result in constant looping with almost no sleep
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
#include "esphome/components/datetime/date_entity.h"
|
#include "esphome/components/datetime/date_entity.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
#include "esphome/components/datetime/time_entity.h"
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
#include "esphome/components/text/text.h"
|
#include "esphome/components/text/text.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -128,6 +131,10 @@ class Application {
|
||||||
void register_date(datetime::DateEntity *date) { this->dates_.push_back(date); }
|
void register_date(datetime::DateEntity *date) { this->dates_.push_back(date); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void register_time(datetime::TimeEntity *time) { this->times_.push_back(time); }
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
void register_text(text::Text *text) { this->texts_.push_back(text); }
|
void register_text(text::Text *text) { this->texts_.push_back(text); }
|
||||||
#endif
|
#endif
|
||||||
|
@ -305,6 +312,15 @@ class Application {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
const std::vector<datetime::TimeEntity *> &get_times() { return this->times_; }
|
||||||
|
datetime::TimeEntity *get_time_by_key(uint32_t key, bool include_internal = false) {
|
||||||
|
for (auto *obj : this->times_)
|
||||||
|
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||||
|
return obj;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
const std::vector<text::Text *> &get_texts() { return this->texts_; }
|
const std::vector<text::Text *> &get_texts() { return this->texts_; }
|
||||||
text::Text *get_text_by_key(uint32_t key, bool include_internal = false) {
|
text::Text *get_text_by_key(uint32_t key, bool include_internal = false) {
|
||||||
|
@ -401,6 +417,9 @@ class Application {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
std::vector<datetime::DateEntity *> dates_{};
|
std::vector<datetime::DateEntity *> dates_{};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
std::vector<datetime::TimeEntity *> times_{};
|
||||||
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
std::vector<select::Select *> selects_{};
|
std::vector<select::Select *> selects_{};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -76,7 +76,12 @@ bool Component::cancel_timeout(const std::string &name) { // NOLINT
|
||||||
|
|
||||||
void Component::call_loop() { this->loop(); }
|
void Component::call_loop() { this->loop(); }
|
||||||
void Component::call_setup() { this->setup(); }
|
void Component::call_setup() { this->setup(); }
|
||||||
void Component::call_dump_config() { this->dump_config(); }
|
void Component::call_dump_config() {
|
||||||
|
this->dump_config();
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, " Component %s is marked FAILED", this->get_component_source());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t Component::get_component_state() const { return this->component_state_; }
|
uint32_t Component::get_component_state() const { return this->component_state_; }
|
||||||
void Component::call() {
|
void Component::call() {
|
||||||
|
@ -149,26 +154,26 @@ void Component::status_set_warning(const char *message) {
|
||||||
return;
|
return;
|
||||||
this->component_state_ |= STATUS_LED_WARNING;
|
this->component_state_ |= STATUS_LED_WARNING;
|
||||||
App.app_state_ |= STATUS_LED_WARNING;
|
App.app_state_ |= STATUS_LED_WARNING;
|
||||||
ESP_LOGW(this->get_component_source(), "Warning set: %s", message);
|
ESP_LOGW(TAG, "Component %s set Warning flag: %s", this->get_component_source(), message);
|
||||||
}
|
}
|
||||||
void Component::status_set_error(const char *message) {
|
void Component::status_set_error(const char *message) {
|
||||||
if ((this->component_state_ & STATUS_LED_ERROR) != 0)
|
if ((this->component_state_ & STATUS_LED_ERROR) != 0)
|
||||||
return;
|
return;
|
||||||
this->component_state_ |= STATUS_LED_ERROR;
|
this->component_state_ |= STATUS_LED_ERROR;
|
||||||
App.app_state_ |= STATUS_LED_ERROR;
|
App.app_state_ |= STATUS_LED_ERROR;
|
||||||
ESP_LOGE(this->get_component_source(), "Error set: %s", message);
|
ESP_LOGE(TAG, "Component %s set Error flag: %s", this->get_component_source(), message);
|
||||||
}
|
}
|
||||||
void Component::status_clear_warning() {
|
void Component::status_clear_warning() {
|
||||||
if ((this->component_state_ & STATUS_LED_WARNING) == 0)
|
if ((this->component_state_ & STATUS_LED_WARNING) == 0)
|
||||||
return;
|
return;
|
||||||
this->component_state_ &= ~STATUS_LED_WARNING;
|
this->component_state_ &= ~STATUS_LED_WARNING;
|
||||||
ESP_LOGW(this->get_component_source(), "Warning cleared");
|
ESP_LOGW(TAG, "Component %s cleared Warning flag", this->get_component_source());
|
||||||
}
|
}
|
||||||
void Component::status_clear_error() {
|
void Component::status_clear_error() {
|
||||||
if ((this->component_state_ & STATUS_LED_ERROR) == 0)
|
if ((this->component_state_ & STATUS_LED_ERROR) == 0)
|
||||||
return;
|
return;
|
||||||
this->component_state_ &= ~STATUS_LED_ERROR;
|
this->component_state_ &= ~STATUS_LED_ERROR;
|
||||||
ESP_LOGE(this->get_component_source(), "Error cleared");
|
ESP_LOGE(TAG, "Component %s cleared Error flag", this->get_component_source());
|
||||||
}
|
}
|
||||||
void Component::status_momentary_warning(const std::string &name, uint32_t length) {
|
void Component::status_momentary_warning(const std::string &name, uint32_t length) {
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
|
|
|
@ -217,6 +217,21 @@ void ComponentIterator::advance() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
case IteratorState::DATETIME_TIME:
|
||||||
|
if (this->at_ >= App.get_times().size()) {
|
||||||
|
advance_platform = true;
|
||||||
|
} else {
|
||||||
|
auto *time = App.get_times()[this->at_];
|
||||||
|
if (time->is_internal() && !this->include_internal_) {
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
success = this->on_time(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
case IteratorState::TEXT:
|
case IteratorState::TEXT:
|
||||||
if (this->at_ >= App.get_texts().size()) {
|
if (this->at_ >= App.get_texts().size()) {
|
||||||
|
|
|
@ -60,6 +60,9 @@ class ComponentIterator {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
virtual bool on_date(datetime::DateEntity *date) = 0;
|
virtual bool on_date(datetime::DateEntity *date) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
virtual bool on_time(datetime::TimeEntity *time) = 0;
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
virtual bool on_text(text::Text *text) = 0;
|
virtual bool on_text(text::Text *text) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
@ -120,6 +123,9 @@ class ComponentIterator {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
DATETIME_DATE,
|
DATETIME_DATE,
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
DATETIME_TIME,
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
TEXT,
|
TEXT,
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -65,6 +65,12 @@ void Controller::setup_controller(bool include_internal) {
|
||||||
obj->add_on_state_callback([this, obj]() { this->on_date_update(obj); });
|
obj->add_on_state_callback([this, obj]() { this->on_date_update(obj); });
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
for (auto *obj : App.get_times()) {
|
||||||
|
if (include_internal || !obj->is_internal())
|
||||||
|
obj->add_on_state_callback([this, obj]() { this->on_time_update(obj); });
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
for (auto *obj : App.get_texts()) {
|
for (auto *obj : App.get_texts()) {
|
||||||
if (include_internal || !obj->is_internal())
|
if (include_internal || !obj->is_internal())
|
||||||
|
|
|
@ -34,6 +34,9 @@
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
#include "esphome/components/datetime/date_entity.h"
|
#include "esphome/components/datetime/date_entity.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
#include "esphome/components/datetime/time_entity.h"
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
#include "esphome/components/text/text.h"
|
#include "esphome/components/text/text.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -85,6 +88,9 @@ class Controller {
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
virtual void on_date_update(datetime::DateEntity *obj){};
|
virtual void on_date_update(datetime::DateEntity *obj){};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
virtual void on_time_update(datetime::TimeEntity *obj){};
|
||||||
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
virtual void on_text_update(text::Text *obj, const std::string &state){};
|
virtual void on_text_update(text::Text *obj, const std::string &state){};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#define USE_NUMBER
|
#define USE_NUMBER
|
||||||
#define USE_DATETIME
|
#define USE_DATETIME
|
||||||
#define USE_DATETIME_DATE
|
#define USE_DATETIME_DATE
|
||||||
|
#define USE_DATETIME_TIME
|
||||||
#define USE_OTA
|
#define USE_OTA
|
||||||
#define USE_OTA_PASSWORD
|
#define USE_OTA_PASSWORD
|
||||||
#define USE_OTA_STATE_CALLBACK
|
#define USE_OTA_STATE_CALLBACK
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
#ifdef USE_DATETIME
|
|
||||||
#include <regex>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "helpers.h"
|
|
||||||
#include "time.h" // NOLINT
|
#include "time.h" // NOLINT
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
|
@ -66,48 +64,47 @@ std::string ESPTime::strftime(const std::string &format) {
|
||||||
return timestr;
|
return timestr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_DATETIME
|
|
||||||
|
|
||||||
bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) {
|
bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) {
|
||||||
// clang-format off
|
uint16_t year;
|
||||||
std::regex dt_regex(R"(^
|
uint8_t month;
|
||||||
(
|
uint8_t day;
|
||||||
(\d{4})-(\d{1,2})-(\d{1,2})
|
uint8_t hour;
|
||||||
(?:\s(?=.+))
|
uint8_t minute;
|
||||||
)?
|
uint8_t second;
|
||||||
(
|
int num;
|
||||||
(\d{1,2}):(\d{2})
|
|
||||||
(?::(\d{2}))?
|
|
||||||
)?
|
|
||||||
$)");
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
std::smatch match;
|
if (sscanf(time_to_parse.c_str(), "%04hu-%02hhu-%02hhu %02hhu:%02hhu:%02hhu %n", &year, &month, &day, // NOLINT
|
||||||
if (std::regex_match(time_to_parse, match, dt_regex) == 0)
|
&hour, // NOLINT
|
||||||
|
&minute, // NOLINT
|
||||||
|
&second, &num) == 6 && // NOLINT
|
||||||
|
num == time_to_parse.size()) {
|
||||||
|
esp_time.year = year;
|
||||||
|
esp_time.month = month;
|
||||||
|
esp_time.day_of_month = day;
|
||||||
|
esp_time.hour = hour;
|
||||||
|
esp_time.minute = minute;
|
||||||
|
esp_time.second = second;
|
||||||
|
} else if (sscanf(time_to_parse.c_str(), "%02hhu:%02hhu:%02hhu %n", &hour, &minute, &second, &num) == 3 && // NOLINT
|
||||||
|
num == time_to_parse.size()) {
|
||||||
|
esp_time.hour = hour;
|
||||||
|
esp_time.minute = minute;
|
||||||
|
esp_time.second = second;
|
||||||
|
} else if (sscanf(time_to_parse.c_str(), "%02hhu:%02hhu %n", &hour, &minute, &num) == 2 && // NOLINT
|
||||||
|
num == time_to_parse.size()) {
|
||||||
|
esp_time.hour = hour;
|
||||||
|
esp_time.minute = minute;
|
||||||
|
esp_time.second = 0;
|
||||||
|
} else if (sscanf(time_to_parse.c_str(), "%04hu-%02hhu-%02hhu %n", &year, &month, &day, &num) == 3 && // NOLINT
|
||||||
|
num == time_to_parse.size()) {
|
||||||
|
esp_time.year = year;
|
||||||
|
esp_time.month = month;
|
||||||
|
esp_time.day_of_month = day;
|
||||||
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (match[1].matched) { // Has date parts
|
|
||||||
|
|
||||||
esp_time.year = parse_number<uint16_t>(match[2].str()).value_or(0);
|
|
||||||
esp_time.month = parse_number<uint8_t>(match[3].str()).value_or(0);
|
|
||||||
esp_time.day_of_month = parse_number<uint8_t>(match[4].str()).value_or(0);
|
|
||||||
}
|
}
|
||||||
if (match[5].matched) { // Has time parts
|
|
||||||
|
|
||||||
esp_time.hour = parse_number<uint8_t>(match[6].str()).value_or(0);
|
|
||||||
esp_time.minute = parse_number<uint8_t>(match[7].str()).value_or(0);
|
|
||||||
if (match[8].matched) {
|
|
||||||
esp_time.second = parse_number<uint8_t>(match[8].str()).value_or(0);
|
|
||||||
} else {
|
|
||||||
esp_time.second = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void ESPTime::increment_second() {
|
void ESPTime::increment_second() {
|
||||||
this->timestamp++;
|
this->timestamp++;
|
||||||
if (!increment_time_value(this->second, 0, 60))
|
if (!increment_time_value(this->second, 0, 60))
|
||||||
|
|
|
@ -67,8 +67,6 @@ struct ESPTime {
|
||||||
this->day_of_year < 367 && this->month > 0 && this->month < 13;
|
this->day_of_year < 367 && this->month > 0 && this->month < 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_DATETIME
|
|
||||||
|
|
||||||
/** Convert a string to ESPTime struct as specified by the format argument.
|
/** Convert a string to ESPTime struct as specified by the format argument.
|
||||||
* @param time_to_parse null-terminated c string formatet like this: 2020-08-25 05:30:00.
|
* @param time_to_parse null-terminated c string formatet like this: 2020-08-25 05:30:00.
|
||||||
* @param esp_time an instance of a ESPTime struct
|
* @param esp_time an instance of a ESPTime struct
|
||||||
|
@ -76,8 +74,6 @@ struct ESPTime {
|
||||||
*/
|
*/
|
||||||
static bool strptime(const std::string &time_to_parse, ESPTime &esp_time);
|
static bool strptime(const std::string &time_to_parse, ESPTime &esp_time);
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Convert a C tm struct instance with a C unix epoch timestamp to an ESPTime instance.
|
/// Convert a C tm struct instance with a C unix epoch timestamp to an ESPTime instance.
|
||||||
static ESPTime from_c_tm(struct tm *c_tm, time_t c_time);
|
static ESPTime from_c_tm(struct tm *c_tm, time_t c_time);
|
||||||
|
|
||||||
|
|
|
@ -59,17 +59,14 @@ def clone_or_update(
|
||||||
)
|
)
|
||||||
|
|
||||||
repo_dir = _compute_destination_path(key, domain)
|
repo_dir = _compute_destination_path(key, domain)
|
||||||
fetch_pr_branch = ref is not None and ref.startswith("pull/")
|
|
||||||
if not repo_dir.is_dir():
|
if not repo_dir.is_dir():
|
||||||
_LOGGER.info("Cloning %s", key)
|
_LOGGER.info("Cloning %s", key)
|
||||||
_LOGGER.debug("Location: %s", repo_dir)
|
_LOGGER.debug("Location: %s", repo_dir)
|
||||||
cmd = ["git", "clone", "--depth=1"]
|
cmd = ["git", "clone", "--depth=1"]
|
||||||
if ref is not None and not fetch_pr_branch:
|
|
||||||
cmd += ["--branch", ref]
|
|
||||||
cmd += ["--", url, str(repo_dir)]
|
cmd += ["--", url, str(repo_dir)]
|
||||||
run_git_command(cmd)
|
run_git_command(cmd)
|
||||||
|
|
||||||
if fetch_pr_branch:
|
if ref is not None:
|
||||||
# We need to fetch the PR branch first, otherwise git will complain
|
# We need to fetch the PR branch first, otherwise git will complain
|
||||||
# about missing objects
|
# about missing objects
|
||||||
_LOGGER.info("Fetching %s", ref)
|
_LOGGER.info("Fetching %s", ref)
|
||||||
|
|
|
@ -154,13 +154,12 @@ extra_scripts = post:esphome/components/esp32/post_build.py.script
|
||||||
; These are common settings for the RP2040 using Arduino.
|
; These are common settings for the RP2040 using Arduino.
|
||||||
[common:rp2040-arduino]
|
[common:rp2040-arduino]
|
||||||
extends = common:arduino
|
extends = common:arduino
|
||||||
board_build.core = earlephilhower
|
|
||||||
board_build.filesystem_size = 0.5m
|
board_build.filesystem_size = 0.5m
|
||||||
|
|
||||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
platform_packages =
|
platform_packages =
|
||||||
; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted
|
; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted
|
||||||
earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.6.0/rp2040-3.6.0.zip
|
earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.7.2/rp2040-3.7.2.zip
|
||||||
|
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
# Useful stuff when working in a development environment
|
# Useful stuff when working in a development environment
|
||||||
clang-format==13.0.1
|
clang-format==13.0.1
|
||||||
clang-tidy==14.0.6
|
clang-tidy==14.0.6
|
||||||
|
yamllint==1.35.1
|
||||||
|
|
|
@ -57,6 +57,7 @@ file_types = (
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
cpp_include = ("*.h", "*.c", "*.cpp", "*.tcc")
|
cpp_include = ("*.h", "*.c", "*.cpp", "*.tcc")
|
||||||
|
py_include = ("*.py",)
|
||||||
ignore_types = (".ico", ".png", ".woff", ".woff2", "")
|
ignore_types = (".ico", ".png", ".woff", ".woff2", "")
|
||||||
|
|
||||||
LINT_FILE_CHECKS = []
|
LINT_FILE_CHECKS = []
|
||||||
|
@ -265,7 +266,8 @@ def lint_end_newline(fname, content):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
CPP_RE_EOL = r"\s*?(?://.*?)?$"
|
CPP_RE_EOL = r".*?(?://.*?)?$"
|
||||||
|
PY_RE_EOL = r".*?(?:#.*?)?$"
|
||||||
|
|
||||||
|
|
||||||
def highlight(s):
|
def highlight(s):
|
||||||
|
@ -273,7 +275,7 @@ def highlight(s):
|
||||||
|
|
||||||
|
|
||||||
@lint_re_check(
|
@lint_re_check(
|
||||||
r"^#define\s+([a-zA-Z0-9_]+)\s+([0-9bx]+)" + CPP_RE_EOL,
|
r"^#define\s+([a-zA-Z0-9_]+)\s+(0b[10]+|0x[0-9a-fA-F]+|\d+)\s*?(?:\/\/.*?)?$",
|
||||||
include=cpp_include,
|
include=cpp_include,
|
||||||
exclude=[
|
exclude=[
|
||||||
"esphome/core/log.h",
|
"esphome/core/log.h",
|
||||||
|
@ -574,11 +576,6 @@ def lint_pragma_once(fname, content):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@lint_re_check(
|
|
||||||
r"(whitelist|blacklist|slave)",
|
|
||||||
exclude=["script/ci-custom.py"],
|
|
||||||
flags=re.IGNORECASE | re.MULTILINE,
|
|
||||||
)
|
|
||||||
def lint_inclusive_language(fname, match):
|
def lint_inclusive_language(fname, match):
|
||||||
# From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=49decddd39e5f6132ccd7d9fdc3d7c470b0061bb
|
# From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=49decddd39e5f6132ccd7d9fdc3d7c470b0061bb
|
||||||
return (
|
return (
|
||||||
|
@ -596,6 +593,21 @@ def lint_inclusive_language(fname, match):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
lint_re_check(
|
||||||
|
r"(whitelist|blacklist|slave)" + PY_RE_EOL,
|
||||||
|
include=py_include,
|
||||||
|
exclude=["script/ci-custom.py"],
|
||||||
|
flags=re.IGNORECASE | re.MULTILINE,
|
||||||
|
)(lint_inclusive_language)
|
||||||
|
|
||||||
|
|
||||||
|
lint_re_check(
|
||||||
|
r"(whitelist|blacklist|slave)" + CPP_RE_EOL,
|
||||||
|
include=cpp_include,
|
||||||
|
flags=re.IGNORECASE | re.MULTILINE,
|
||||||
|
)(lint_inclusive_language)
|
||||||
|
|
||||||
|
|
||||||
@lint_re_check(r"[\t\r\f\v ]+$")
|
@lint_re_check(r"[\t\r\f\v ]+$")
|
||||||
def lint_trailing_whitespace(fname, match):
|
def lint_trailing_whitespace(fname, match):
|
||||||
return "Trailing whitespace detected"
|
return "Trailing whitespace detected"
|
||||||
|
@ -610,6 +622,7 @@ def lint_trailing_whitespace(fname, match):
|
||||||
"esphome/components/climate/climate.h",
|
"esphome/components/climate/climate.h",
|
||||||
"esphome/components/cover/cover.h",
|
"esphome/components/cover/cover.h",
|
||||||
"esphome/components/datetime/date_entity.h",
|
"esphome/components/datetime/date_entity.h",
|
||||||
|
"esphome/components/datetime/time_entity.h",
|
||||||
"esphome/components/display/display.h",
|
"esphome/components/display/display.h",
|
||||||
"esphome/components/fan/fan.h",
|
"esphome/components/fan/fan.h",
|
||||||
"esphome/components/i2c/i2c.h",
|
"esphome/components/i2c/i2c.h",
|
||||||
|
|
|
@ -14,6 +14,8 @@ sensor:
|
||||||
name: QMC5883L Field Strength Z
|
name: QMC5883L Field Strength Z
|
||||||
heading:
|
heading:
|
||||||
name: QMC5883L Heading
|
name: QMC5883L Heading
|
||||||
|
temperature:
|
||||||
|
name: QMC5883L Temperature
|
||||||
range: 800uT
|
range: 800uT
|
||||||
oversampling: 256x
|
oversampling: 256x
|
||||||
update_interval: 15s
|
update_interval: 15s
|
||||||
|
|
|
@ -14,6 +14,8 @@ sensor:
|
||||||
name: QMC5883L Field Strength Z
|
name: QMC5883L Field Strength Z
|
||||||
heading:
|
heading:
|
||||||
name: QMC5883L Heading
|
name: QMC5883L Heading
|
||||||
|
temperature:
|
||||||
|
name: QMC5883L Temperature
|
||||||
range: 800uT
|
range: 800uT
|
||||||
oversampling: 256x
|
oversampling: 256x
|
||||||
update_interval: 15s
|
update_interval: 15s
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue