mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 06:33:51 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/dev' into nrf52
This commit is contained in:
		
							
								
								
									
										84
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										84
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -20,7 +20,6 @@ permissions: | ||||
| env: | ||||
|   DEFAULT_PYTHON: "3.10" | ||||
|   PYUPGRADE_TARGET: "--py39-plus" | ||||
|   CLANG_FORMAT_VERSION: "13.0.1" | ||||
|  | ||||
| concurrency: | ||||
|   # yamllint disable-line rule:line-length | ||||
| @@ -239,7 +238,7 @@ jobs: | ||||
|       - name: Install clang-format | ||||
|         run: | | ||||
|           . venv/bin/activate | ||||
|           pip install clang-format==${{ env.CLANG_FORMAT_VERSION }} | ||||
|           pip install clang-format -c requirements_dev.txt | ||||
|       - name: Run clang-format | ||||
|         run: | | ||||
|           . venv/bin/activate | ||||
| @@ -399,7 +398,8 @@ jobs: | ||||
|       - common | ||||
|     if: github.event_name == 'pull_request' | ||||
|     outputs: | ||||
|       matrix: ${{ steps.set-matrix.outputs.matrix }} | ||||
|       components: ${{ steps.list-components.outputs.components }} | ||||
|       count: ${{ steps.list-components.outputs.count }} | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
| @@ -420,10 +420,18 @@ jobs: | ||||
|           python-version: ${{ env.DEFAULT_PYTHON }} | ||||
|           cache-key: ${{ needs.common.outputs.cache-key }} | ||||
|       - name: Find changed components | ||||
|         id: set-matrix | ||||
|         id: list-components | ||||
|         run: | | ||||
|           . venv/bin/activate | ||||
|           echo "matrix=$(script/list-components.py --changed --branch ${{ steps.target-branch.outputs.branch }} | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT | ||||
|           components=$(script/list-components.py --changed --branch ${{ steps.target-branch.outputs.branch }}) | ||||
|           output_components=$(echo "$components" | jq -R -s -c 'split("\n")[:-1] | map(select(length > 0))') | ||||
|           count=$(echo "$output_components" | jq length) | ||||
|  | ||||
|           echo "components=$output_components" >> $GITHUB_OUTPUT | ||||
|           echo "count=$count" >> $GITHUB_OUTPUT | ||||
|  | ||||
|           echo "$count Components:" | ||||
|           echo "$output_components" | jq | ||||
|  | ||||
|   test-build-components: | ||||
|     name: Component test ${{ matrix.file }} | ||||
| @@ -431,12 +439,12 @@ jobs: | ||||
|     needs: | ||||
|       - common | ||||
|       - list-components | ||||
|     if: ${{ github.event_name == 'pull_request' && needs.list-components.outputs.matrix != '[]' && needs.list-components.outputs.matrix != '' }} | ||||
|     if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) > 0 && fromJSON(needs.list-components.outputs.count) < 100 | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       max-parallel: 2 | ||||
|       matrix: | ||||
|         file: ${{ fromJson(needs.list-components.outputs.matrix) }} | ||||
|         file: ${{ fromJson(needs.list-components.outputs.components) }} | ||||
|     steps: | ||||
|       - name: Install libsodium | ||||
|         run: sudo apt-get install libsodium-dev | ||||
| @@ -457,6 +465,64 @@ jobs: | ||||
|           . venv/bin/activate | ||||
|           ./script/test_build_components -e compile -c ${{ matrix.file }} | ||||
|  | ||||
|   test-build-components-splitter: | ||||
|     name: Split components for testing into 20 groups maximum | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: | ||||
|       - common | ||||
|       - list-components | ||||
|     if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) >= 100 | ||||
|     outputs: | ||||
|       matrix: ${{ steps.split.outputs.components }} | ||||
|     steps: | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|       - name: Split components into 20 groups | ||||
|         id: split | ||||
|         run: | | ||||
|           components=$(echo '${{ needs.list-components.outputs.components }}' | jq -c '.[]' | shuf | jq -s -c '[_nwise(20) | join(" ")]') | ||||
|           echo "components=$components" >> $GITHUB_OUTPUT | ||||
|  | ||||
|   test-build-components-split: | ||||
|     name: Test split components | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: | ||||
|       - common | ||||
|       - list-components | ||||
|       - test-build-components-splitter | ||||
|     if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) >= 100 | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       max-parallel: 4 | ||||
|       matrix: | ||||
|         components: ${{ fromJson(needs.test-build-components-splitter.outputs.matrix) }} | ||||
|     steps: | ||||
|       - name: List components | ||||
|         run: echo ${{ matrix.components }} | ||||
|  | ||||
|       - name: Install libsodium | ||||
|         run: sudo apt-get install libsodium-dev | ||||
|  | ||||
|       - name: Check out code from GitHub | ||||
|         uses: actions/checkout@v4.1.1 | ||||
|       - name: Restore Python | ||||
|         uses: ./.github/actions/restore-python | ||||
|         with: | ||||
|           python-version: ${{ env.DEFAULT_PYTHON }} | ||||
|           cache-key: ${{ needs.common.outputs.cache-key }} | ||||
|       - name: Validate config | ||||
|         run: | | ||||
|           . venv/bin/activate | ||||
|           for component in ${{ matrix.components }}; do | ||||
|             ./script/test_build_components -e config -c $component | ||||
|           done | ||||
|       - name: Compile config | ||||
|         run: | | ||||
|           . venv/bin/activate | ||||
|           for component in ${{ matrix.components }}; do | ||||
|             ./script/test_build_components -e compile -c $component | ||||
|           done | ||||
|  | ||||
|   ci-status: | ||||
|     name: CI Status | ||||
|     runs-on: ubuntu-latest | ||||
| @@ -471,8 +537,10 @@ jobs: | ||||
|       - pyupgrade | ||||
|       - compile-tests | ||||
|       - clang-tidy | ||||
|       - test-build-components | ||||
|       - list-components | ||||
|       - test-build-components | ||||
|       - test-build-components-splitter | ||||
|       - test-build-components-split | ||||
|     if: always() | ||||
|     steps: | ||||
|       - name: Success | ||||
|   | ||||
| @@ -31,3 +31,12 @@ repos: | ||||
|     hooks: | ||||
|       - id: pyupgrade | ||||
|         args: [--py39-plus] | ||||
|   - repo: https://github.com/adrienverge/yamllint.git | ||||
|     rev: v1.35.1 | ||||
|     hooks: | ||||
|       - id: yamllint | ||||
|   - repo: https://github.com/pre-commit/mirrors-clang-format | ||||
|     rev: v13.0.1 | ||||
|     hooks: | ||||
|       - id: clang-format | ||||
|         types_or: [c, c++] | ||||
|   | ||||
							
								
								
									
										14
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								CODEOWNERS
									
									
									
									
									
								
							| @@ -119,6 +119,7 @@ esphome/components/esp32_rmt/* @jesserockz | ||||
| esphome/components/esp32_rmt_led_strip/* @jesserockz | ||||
| esphome/components/esp8266/* @esphome/core | ||||
| esphome/components/ethernet_info/* @gtjadsonsantos | ||||
| esphome/components/event/* @nohat | ||||
| esphome/components/exposure_notifications/* @OttoWinter | ||||
| esphome/components/ezo/* @ssieb | ||||
| esphome/components/ezo_pmp/* @carlos-sarmiento | ||||
| @@ -359,6 +360,7 @@ esphome/components/tee501/* @Stock-M | ||||
| esphome/components/teleinfo/* @0hax | ||||
| esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar | ||||
| esphome/components/template/datetime/* @rfdarter | ||||
| esphome/components/template/event/* @nohat | ||||
| esphome/components/template/fan/* @ssieb | ||||
| esphome/components/text/* @mauritskorse | ||||
| esphome/components/thermostat/* @kbx81 | ||||
| @@ -390,6 +392,7 @@ esphome/components/ufire_ec/* @pvizeli | ||||
| esphome/components/ufire_ise/* @pvizeli | ||||
| esphome/components/ultrasonic/* @OttoWinter | ||||
| esphome/components/uponor_smatrix/* @kroimon | ||||
| esphome/components/valve/* @esphome/core | ||||
| esphome/components/vbus/* @ssieb | ||||
| esphome/components/veml3235/* @kbx81 | ||||
| esphome/components/veml7700/* @latonita | ||||
| @@ -399,10 +402,21 @@ esphome/components/wake_on_lan/* @willwill2will54 | ||||
| esphome/components/waveshare_epaper/* @clydebarrow | ||||
| esphome/components/web_server_base/* @OttoWinter | ||||
| esphome/components/web_server_idf/* @dentra | ||||
| esphome/components/weikai/* @DrCoolZic | ||||
| esphome/components/weikai_i2c/* @DrCoolZic | ||||
| esphome/components/weikai_spi/* @DrCoolZic | ||||
| esphome/components/whirlpool/* @glmnet | ||||
| esphome/components/whynter/* @aeonsablaze | ||||
| esphome/components/wiegand/* @ssieb | ||||
| esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard | ||||
| esphome/components/wk2132_i2c/* @DrCoolZic | ||||
| esphome/components/wk2132_spi/* @DrCoolZic | ||||
| esphome/components/wk2168_i2c/* @DrCoolZic | ||||
| esphome/components/wk2168_spi/* @DrCoolZic | ||||
| esphome/components/wk2204_i2c/* @DrCoolZic | ||||
| esphome/components/wk2204_spi/* @DrCoolZic | ||||
| esphome/components/wk2212_i2c/* @DrCoolZic | ||||
| esphome/components/wk2212_spi/* @DrCoolZic | ||||
| esphome/components/wl_134/* @hobbypunk90 | ||||
| esphome/components/x9c/* @EtienneMD | ||||
| esphome/components/xgzp68xx/* @gcormier | ||||
|   | ||||
| @@ -43,6 +43,7 @@ service APIConnection { | ||||
|   rpc select_command (SelectCommandRequest) returns (void) {} | ||||
|   rpc button_command (ButtonCommandRequest) returns (void) {} | ||||
|   rpc lock_command (LockCommandRequest) returns (void) {} | ||||
|   rpc valve_command (ValveCommandRequest) returns (void) {} | ||||
|   rpc media_player_command (MediaPlayerCommandRequest) returns (void) {} | ||||
|   rpc date_command (DateCommandRequest) returns (void) {} | ||||
|   rpc time_command (TimeCommandRequest) returns (void) {} | ||||
| @@ -1700,3 +1701,79 @@ message TimeCommandRequest { | ||||
|   uint32 minute = 3; | ||||
|   uint32 second = 4; | ||||
| } | ||||
|  | ||||
| // ==================== EVENT ==================== | ||||
| message ListEntitiesEventResponse { | ||||
|   option (id) = 107; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_EVENT"; | ||||
|  | ||||
|   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; | ||||
|   string device_class = 8; | ||||
|  | ||||
|   repeated string event_types = 9; | ||||
| } | ||||
| message EventResponse { | ||||
|   option (id) = 108; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_EVENT"; | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   string event_type = 2; | ||||
| } | ||||
|  | ||||
| // ==================== VALVE ==================== | ||||
| message ListEntitiesValveResponse { | ||||
|   option (id) = 109; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_VALVE"; | ||||
|  | ||||
|   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; | ||||
|   string device_class = 8; | ||||
|  | ||||
|   bool assumed_state = 9; | ||||
|   bool supports_position = 10; | ||||
|   bool supports_stop = 11; | ||||
| } | ||||
|  | ||||
| enum ValveOperation { | ||||
|   VALVE_OPERATION_IDLE = 0; | ||||
|   VALVE_OPERATION_IS_OPENING = 1; | ||||
|   VALVE_OPERATION_IS_CLOSING = 2; | ||||
| } | ||||
| message ValveStateResponse { | ||||
|   option (id) = 110; | ||||
|   option (source) = SOURCE_SERVER; | ||||
|   option (ifdef) = "USE_VALVE"; | ||||
|   option (no_delay) = true; | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   float position = 2; | ||||
|   ValveOperation current_operation = 3; | ||||
| } | ||||
|  | ||||
| message ValveCommandRequest { | ||||
|   option (id) = 111; | ||||
|   option (source) = SOURCE_CLIENT; | ||||
|   option (ifdef) = "USE_VALVE"; | ||||
|   option (no_delay) = true; | ||||
|  | ||||
|   fixed32 key = 1; | ||||
|   bool has_position = 2; | ||||
|   float position = 3; | ||||
|   bool stop = 4; | ||||
| } | ||||
|   | ||||
| @@ -915,6 +915,48 @@ void APIConnection::lock_command(const LockCommandRequest &msg) { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_VALVE | ||||
| bool APIConnection::send_valve_state(valve::Valve *valve) { | ||||
|   if (!this->state_subscription_) | ||||
|     return false; | ||||
|  | ||||
|   ValveStateResponse resp{}; | ||||
|   resp.key = valve->get_object_id_hash(); | ||||
|   resp.position = valve->position; | ||||
|   resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation); | ||||
|   return this->send_valve_state_response(resp); | ||||
| } | ||||
| bool APIConnection::send_valve_info(valve::Valve *valve) { | ||||
|   auto traits = valve->get_traits(); | ||||
|   ListEntitiesValveResponse msg; | ||||
|   msg.key = valve->get_object_id_hash(); | ||||
|   msg.object_id = valve->get_object_id(); | ||||
|   if (valve->has_own_name()) | ||||
|     msg.name = valve->get_name(); | ||||
|   msg.unique_id = get_default_unique_id("valve", valve); | ||||
|   msg.icon = valve->get_icon(); | ||||
|   msg.disabled_by_default = valve->is_disabled_by_default(); | ||||
|   msg.entity_category = static_cast<enums::EntityCategory>(valve->get_entity_category()); | ||||
|   msg.device_class = valve->get_device_class(); | ||||
|   msg.assumed_state = traits.get_is_assumed_state(); | ||||
|   msg.supports_position = traits.get_supports_position(); | ||||
|   msg.supports_stop = traits.get_supports_stop(); | ||||
|   return this->send_list_entities_valve_response(msg); | ||||
| } | ||||
| void APIConnection::valve_command(const ValveCommandRequest &msg) { | ||||
|   valve::Valve *valve = App.get_valve_by_key(msg.key); | ||||
|   if (valve == nullptr) | ||||
|     return; | ||||
|  | ||||
|   auto call = valve->make_call(); | ||||
|   if (msg.has_position) | ||||
|     call.set_position(msg.position); | ||||
|   if (msg.stop) | ||||
|     call.set_command_stop(); | ||||
|   call.perform(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) { | ||||
|   if (!this->state_subscription_) | ||||
| @@ -1167,6 +1209,30 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
| bool APIConnection::send_event(event::Event *event, std::string event_type) { | ||||
|   EventResponse resp{}; | ||||
|   resp.key = event->get_object_id_hash(); | ||||
|   resp.event_type = std::move(event_type); | ||||
|   return this->send_event_response(resp); | ||||
| } | ||||
| bool APIConnection::send_event_info(event::Event *event) { | ||||
|   ListEntitiesEventResponse msg; | ||||
|   msg.key = event->get_object_id_hash(); | ||||
|   msg.object_id = event->get_object_id(); | ||||
|   if (event->has_own_name()) | ||||
|     msg.name = event->get_name(); | ||||
|   msg.unique_id = get_default_unique_id("event", event); | ||||
|   msg.icon = event->get_icon(); | ||||
|   msg.disabled_by_default = event->is_disabled_by_default(); | ||||
|   msg.entity_category = static_cast<enums::EntityCategory>(event->get_entity_category()); | ||||
|   msg.device_class = event->get_device_class(); | ||||
|   for (const auto &event_type : event->get_event_types()) | ||||
|     msg.event_types.push_back(event_type); | ||||
|   return this->send_list_entities_event_response(msg); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| bool APIConnection::send_log_message(int level, const char *tag, const char *line) { | ||||
|   if (this->log_subscription_ < level) | ||||
|     return false; | ||||
|   | ||||
| @@ -101,6 +101,11 @@ class APIConnection : public APIServerConnection { | ||||
|   bool send_lock_info(lock::Lock *a_lock); | ||||
|   void lock_command(const LockCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool send_valve_state(valve::Valve *valve); | ||||
|   bool send_valve_info(valve::Valve *valve); | ||||
|   void valve_command(const ValveCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   bool send_media_player_state(media_player::MediaPlayer *media_player); | ||||
|   bool send_media_player_info(media_player::MediaPlayer *media_player); | ||||
| @@ -148,6 +153,11 @@ class APIConnection : public APIServerConnection { | ||||
|   void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override; | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
|   bool send_event(event::Event *event, std::string event_type); | ||||
|   bool send_event_info(event::Event *event); | ||||
| #endif | ||||
|  | ||||
|   void on_disconnect_response(const DisconnectResponse &value) override; | ||||
|   void on_ping_response(const PingResponse &value) override { | ||||
|     // we initiated ping | ||||
|   | ||||
| @@ -537,6 +537,20 @@ template<> const char *proto_enum_to_string<enums::TextMode>(enums::TextMode val | ||||
|   } | ||||
| } | ||||
| #endif | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| template<> const char *proto_enum_to_string<enums::ValveOperation>(enums::ValveOperation value) { | ||||
|   switch (value) { | ||||
|     case enums::VALVE_OPERATION_IDLE: | ||||
|       return "VALVE_OPERATION_IDLE"; | ||||
|     case enums::VALVE_OPERATION_IS_OPENING: | ||||
|       return "VALVE_OPERATION_IS_OPENING"; | ||||
|     case enums::VALVE_OPERATION_IS_CLOSING: | ||||
|       return "VALVE_OPERATION_IS_CLOSING"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| #endif | ||||
| bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
| @@ -7695,6 +7709,390 @@ void TimeCommandRequest::dump_to(std::string &out) const { | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool ListEntitiesEventResponse::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 ListEntitiesEventResponse::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; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->device_class = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->event_types.push_back(value.as_string()); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ListEntitiesEventResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void ListEntitiesEventResponse::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); | ||||
|   buffer.encode_string(8, this->device_class); | ||||
|   for (auto &it : this->event_types) { | ||||
|     buffer.encode_string(9, it, true); | ||||
|   } | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ListEntitiesEventResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ListEntitiesEventResponse {\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("  device_class: "); | ||||
|   out.append("'").append(this->device_class).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   for (const auto &it : this->event_types) { | ||||
|     out.append("  event_types: "); | ||||
|     out.append("'").append(it).append("'"); | ||||
|     out.append("\n"); | ||||
|   } | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool EventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->event_type = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool EventResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void EventResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_string(2, this->event_type); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void EventResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("EventResponse {\n"); | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  event_type: "); | ||||
|   out.append("'").append(this->event_type).append("'"); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool ListEntitiesValveResponse::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; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->assumed_state = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 10: { | ||||
|       this->supports_position = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 11: { | ||||
|       this->supports_stop = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ListEntitiesValveResponse::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; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->device_class = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ListEntitiesValveResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void ListEntitiesValveResponse::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); | ||||
|   buffer.encode_string(8, this->device_class); | ||||
|   buffer.encode_bool(9, this->assumed_state); | ||||
|   buffer.encode_bool(10, this->supports_position); | ||||
|   buffer.encode_bool(11, this->supports_stop); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ListEntitiesValveResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ListEntitiesValveResponse {\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("  device_class: "); | ||||
|   out.append("'").append(this->device_class).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  assumed_state: "); | ||||
|   out.append(YESNO(this->assumed_state)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  supports_position: "); | ||||
|   out.append(YESNO(this->supports_position)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  supports_stop: "); | ||||
|   out.append(YESNO(this->supports_stop)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool ValveStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 3: { | ||||
|       this->current_operation = value.as_enum<enums::ValveOperation>(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ValveStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     case 2: { | ||||
|       this->position = value.as_float(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void ValveStateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_float(2, this->position); | ||||
|   buffer.encode_enum<enums::ValveOperation>(3, this->current_operation); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ValveStateResponse::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ValveStateResponse {\n"); | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  position: "); | ||||
|   sprintf(buffer, "%g", this->position); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  current_operation: "); | ||||
|   out.append(proto_enum_to_string<enums::ValveOperation>(this->current_operation)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| bool ValveCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { | ||||
|   switch (field_id) { | ||||
|     case 2: { | ||||
|       this->has_position = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 4: { | ||||
|       this->stop = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| bool ValveCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
|       this->key = value.as_fixed32(); | ||||
|       return true; | ||||
|     } | ||||
|     case 3: { | ||||
|       this->position = value.as_float(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
| void ValveCommandRequest::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_fixed32(1, this->key); | ||||
|   buffer.encode_bool(2, this->has_position); | ||||
|   buffer.encode_float(3, this->position); | ||||
|   buffer.encode_bool(4, this->stop); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ValveCommandRequest::dump_to(std::string &out) const { | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ValveCommandRequest {\n"); | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->key); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  has_position: "); | ||||
|   out.append(YESNO(this->has_position)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  position: "); | ||||
|   sprintf(buffer, "%g", this->position); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  stop: "); | ||||
|   out.append(YESNO(this->stop)); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| }  // namespace api | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -216,6 +216,11 @@ enum TextMode : uint32_t { | ||||
|   TEXT_MODE_TEXT = 0, | ||||
|   TEXT_MODE_PASSWORD = 1, | ||||
| }; | ||||
| enum ValveOperation : uint32_t { | ||||
|   VALVE_OPERATION_IDLE = 0, | ||||
|   VALVE_OPERATION_IS_OPENING = 1, | ||||
|   VALVE_OPERATION_IS_CLOSING = 2, | ||||
| }; | ||||
|  | ||||
| }  // namespace enums | ||||
|  | ||||
| @@ -1969,6 +1974,92 @@ class TimeCommandRequest : public ProtoMessage { | ||||
|   bool decode_32bit(uint32_t field_id, Proto32Bit value) override; | ||||
|   bool decode_varint(uint32_t field_id, ProtoVarInt value) override; | ||||
| }; | ||||
| class ListEntitiesEventResponse : 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{}; | ||||
|   std::string device_class{}; | ||||
|   std::vector<std::string> event_types{}; | ||||
|   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 EventResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0}; | ||||
|   std::string event_type{}; | ||||
|   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; | ||||
| }; | ||||
| class ListEntitiesValveResponse : 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{}; | ||||
|   std::string device_class{}; | ||||
|   bool assumed_state{false}; | ||||
|   bool supports_position{false}; | ||||
|   bool supports_stop{false}; | ||||
|   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 ValveStateResponse : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0}; | ||||
|   float position{0.0f}; | ||||
|   enums::ValveOperation current_operation{}; | ||||
|   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 ValveCommandRequest : public ProtoMessage { | ||||
|  public: | ||||
|   uint32_t key{0}; | ||||
|   bool has_position{false}; | ||||
|   float position{0.0f}; | ||||
|   bool stop{false}; | ||||
|   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 esphome | ||||
|   | ||||
| @@ -557,6 +557,40 @@ bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse & | ||||
| #endif | ||||
| #ifdef USE_DATETIME_TIME | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
| bool APIServerConnectionBase::send_list_entities_event_response(const ListEntitiesEventResponse &msg) { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   ESP_LOGVV(TAG, "send_list_entities_event_response: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|   return this->send_message_<ListEntitiesEventResponse>(msg, 107); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
| bool APIServerConnectionBase::send_event_response(const EventResponse &msg) { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   ESP_LOGVV(TAG, "send_event_response: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|   return this->send_message_<EventResponse>(msg, 108); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| bool APIServerConnectionBase::send_list_entities_valve_response(const ListEntitiesValveResponse &msg) { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   ESP_LOGVV(TAG, "send_list_entities_valve_response: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|   return this->send_message_<ListEntitiesValveResponse>(msg, 109); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| bool APIServerConnectionBase::send_valve_state_response(const ValveStateResponse &msg) { | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   ESP_LOGVV(TAG, "send_valve_state_response: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|   return this->send_message_<ValveStateResponse>(msg, 110); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| #endif | ||||
| bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { | ||||
|   switch (msg_type) { | ||||
|     case 1: { | ||||
| @@ -1019,6 +1053,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, | ||||
|       ESP_LOGVV(TAG, "on_voice_assistant_audio: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|       this->on_voice_assistant_audio(msg); | ||||
| #endif | ||||
|       break; | ||||
|     } | ||||
|     case 111: { | ||||
| #ifdef USE_VALVE | ||||
|       ValveCommandRequest msg; | ||||
|       msg.decode(msg_data, msg_size); | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|       ESP_LOGVV(TAG, "on_valve_command_request: %s", msg.dump().c_str()); | ||||
| #endif | ||||
|       this->on_valve_command_request(msg); | ||||
| #endif | ||||
|       break; | ||||
|     } | ||||
| @@ -1282,6 +1327,19 @@ void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) | ||||
|   this->lock_command(msg); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) { | ||||
|   if (!this->is_connection_setup()) { | ||||
|     this->on_no_setup_connection(); | ||||
|     return; | ||||
|   } | ||||
|   if (!this->is_authenticated()) { | ||||
|     this->on_unauthenticated_access(); | ||||
|     return; | ||||
|   } | ||||
|   this->valve_command(msg); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) { | ||||
|   if (!this->is_connection_setup()) { | ||||
|   | ||||
| @@ -279,6 +279,21 @@ class APIServerConnectionBase : public ProtoService { | ||||
| #endif | ||||
| #ifdef USE_DATETIME_TIME | ||||
|   virtual void on_time_command_request(const TimeCommandRequest &value){}; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   bool send_list_entities_event_response(const ListEntitiesEventResponse &msg); | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   bool send_event_response(const EventResponse &msg); | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool send_list_entities_valve_response(const ListEntitiesValveResponse &msg); | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool send_valve_state_response(const ValveStateResponse &msg); | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   virtual void on_valve_command_request(const ValveCommandRequest &value){}; | ||||
| #endif | ||||
|  protected: | ||||
|   bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; | ||||
| @@ -331,6 +346,9 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
| #ifdef USE_LOCK | ||||
|   virtual void lock_command(const LockCommandRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   virtual void valve_command(const ValveCommandRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0; | ||||
| #endif | ||||
| @@ -423,6 +441,9 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
| #ifdef USE_LOCK | ||||
|   void on_lock_command_request(const LockCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   void on_valve_command_request(const ValveCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override; | ||||
| #endif | ||||
|   | ||||
| @@ -300,6 +300,15 @@ void APIServer::on_lock_update(lock::Lock *obj) { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_VALVE | ||||
| void APIServer::on_valve_update(valve::Valve *obj) { | ||||
|   if (obj->is_internal()) | ||||
|     return; | ||||
|   for (auto &c : this->clients_) | ||||
|     c->send_valve_state(obj); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| void APIServer::on_media_player_update(media_player::MediaPlayer *obj) { | ||||
|   if (obj->is_internal()) | ||||
| @@ -309,6 +318,13 @@ void APIServer::on_media_player_update(media_player::MediaPlayer *obj) { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
| void APIServer::on_event(event::Event *obj, const std::string &event_type) { | ||||
|   for (auto &c : this->clients_) | ||||
|     c->send_event(obj, event_type); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; } | ||||
| void APIServer::set_port(uint16_t port) { this->port_ = port; } | ||||
| APIServer *global_api_server = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | ||||
|   | ||||
| @@ -81,6 +81,9 @@ class APIServer : public Component, public Controller { | ||||
| #ifdef USE_LOCK | ||||
|   void on_lock_update(lock::Lock *obj) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   void on_valve_update(valve::Valve *obj) override; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   void on_media_player_update(media_player::MediaPlayer *obj) override; | ||||
| #endif | ||||
| @@ -93,6 +96,9 @@ class APIServer : public Component, public Controller { | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   void on_event(event::Event *obj, const std::string &event_type) override; | ||||
| #endif | ||||
|  | ||||
|   bool is_connected() const; | ||||
|  | ||||
|   | ||||
| @@ -38,6 +38,9 @@ bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) | ||||
| #ifdef USE_LOCK | ||||
| bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_info(a_lock); } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| bool ListEntitiesIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_info(valve); } | ||||
| #endif | ||||
|  | ||||
| bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); } | ||||
| ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {} | ||||
| @@ -86,6 +89,9 @@ bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont | ||||
|   return this->client_->send_alarm_control_panel_info(a_alarm_control_panel); | ||||
| } | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
| bool ListEntitiesIterator::on_event(event::Event *event) { return this->client_->send_event_info(event); } | ||||
| #endif | ||||
|  | ||||
| }  // namespace api | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -61,11 +61,17 @@ class ListEntitiesIterator : public ComponentIterator { | ||||
| #ifdef USE_LOCK | ||||
|   bool on_lock(lock::Lock *a_lock) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool on_valve(valve::Valve *valve) override; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   bool on_media_player(media_player::MediaPlayer *media_player) override; | ||||
| #endif | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   bool on_event(event::Event *event) override; | ||||
| #endif | ||||
|   bool on_end() override; | ||||
|  | ||||
|   | ||||
| @@ -59,6 +59,9 @@ bool InitialStateIterator::on_select(select::Select *select) { | ||||
| #ifdef USE_LOCK | ||||
| bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(valve); } | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| bool InitialStateIterator::on_media_player(media_player::MediaPlayer *media_player) { | ||||
|   return this->client_->send_media_player_state(media_player); | ||||
|   | ||||
| @@ -58,11 +58,17 @@ class InitialStateIterator : public ComponentIterator { | ||||
| #ifdef USE_LOCK | ||||
|   bool on_lock(lock::Lock *a_lock) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool on_valve(valve::Valve *valve) override; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   bool on_media_player(media_player::MediaPlayer *media_player) override; | ||||
| #endif | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   bool on_event(event::Event *event) override { return true; }; | ||||
| #endif | ||||
|  protected: | ||||
|   APIConnection *client_; | ||||
|   | ||||
| @@ -5,17 +5,13 @@ namespace cst226 { | ||||
|  | ||||
| void CST226Touchscreen::setup() { | ||||
|   esph_log_config(TAG, "Setting up CST226 Touchscreen..."); | ||||
|   if (this->reset_pin_ != nullptr) { | ||||
|     this->reset_pin_->setup(); | ||||
|     this->reset_pin_->digital_write(true); | ||||
|     delay(5); | ||||
|     this->reset_pin_->digital_write(false); | ||||
|     delay(5); | ||||
|     this->reset_pin_->digital_write(true); | ||||
|     this->set_timeout(30, [this] { this->continue_setup_(); }); | ||||
|   } else { | ||||
|     this->continue_setup_(); | ||||
|   } | ||||
|   this->reset_pin_->setup(); | ||||
|   this->reset_pin_->digital_write(true); | ||||
|   delay(5); | ||||
|   this->reset_pin_->digital_write(false); | ||||
|   delay(5); | ||||
|   this->reset_pin_->digital_write(true); | ||||
|   this->set_timeout(30, [this] { this->continue_setup_(); }); | ||||
| } | ||||
|  | ||||
| void CST226Touchscreen::update_touches() { | ||||
|   | ||||
| @@ -35,7 +35,7 @@ class CST226Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice | ||||
|   void continue_setup_(); | ||||
|  | ||||
|   InternalGPIOPin *interrupt_pin_{}; | ||||
|   GPIOPin *reset_pin_{}; | ||||
|   GPIOPin *reset_pin_{NULL_PIN}; | ||||
|   uint8_t chip_id_{}; | ||||
|   bool setup_complete_{}; | ||||
| }; | ||||
|   | ||||
| @@ -317,17 +317,26 @@ def _parse_platform_version(value): | ||||
|  | ||||
|  | ||||
| def _detect_variant(value): | ||||
|     if CONF_VARIANT not in value: | ||||
|         board = value[CONF_BOARD] | ||||
|         if board not in BOARDS: | ||||
|     board = value[CONF_BOARD] | ||||
|     if board in BOARDS: | ||||
|         variant = BOARDS[board][KEY_VARIANT] | ||||
|         if CONF_VARIANT in value and variant != value[CONF_VARIANT]: | ||||
|             raise cv.Invalid( | ||||
|                 "This board is unknown, please set the variant manually", | ||||
|                 f"Option '{CONF_VARIANT}' does not match selected board.", | ||||
|                 path=[CONF_VARIANT], | ||||
|             ) | ||||
|         value = value.copy() | ||||
|         value[CONF_VARIANT] = variant | ||||
|     else: | ||||
|         if CONF_VARIANT not in value: | ||||
|             raise cv.Invalid( | ||||
|                 "This board is unknown, if you are sure you want to compile with this board selection, " | ||||
|                 f"override with option '{CONF_VARIANT}'", | ||||
|                 path=[CONF_BOARD], | ||||
|             ) | ||||
|  | ||||
|         value = value.copy() | ||||
|         value[CONF_VARIANT] = BOARDS[board][KEY_VARIANT] | ||||
|  | ||||
|         _LOGGER.warning( | ||||
|             "This board is unknown. Make sure the chosen chip component is correct.", | ||||
|         ) | ||||
|     return value | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -364,7 +364,11 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga | ||||
| } | ||||
|  | ||||
| void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) { | ||||
|   this->scan_set_param_failed_ = param.status; | ||||
|   if (param.status == ESP_BT_STATUS_DONE) { | ||||
|     this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS; | ||||
|   } else { | ||||
|     this->scan_set_param_failed_ = param.status; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param ¶m) { | ||||
|   | ||||
							
								
								
									
										134
									
								
								esphome/components/event/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								esphome/components/event/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| from esphome.components import mqtt | ||||
| from esphome.const import ( | ||||
|     CONF_DEVICE_CLASS, | ||||
|     CONF_ENTITY_CATEGORY, | ||||
|     CONF_ICON, | ||||
|     CONF_ID, | ||||
|     CONF_ON_EVENT, | ||||
|     CONF_TRIGGER_ID, | ||||
|     CONF_MQTT_ID, | ||||
|     CONF_EVENT_TYPE, | ||||
|     DEVICE_CLASS_BUTTON, | ||||
|     DEVICE_CLASS_DOORBELL, | ||||
|     DEVICE_CLASS_EMPTY, | ||||
|     DEVICE_CLASS_MOTION, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.cpp_helpers import setup_entity | ||||
| from esphome.cpp_generator import MockObjClass | ||||
|  | ||||
| CODEOWNERS = ["@nohat"] | ||||
| IS_PLATFORM_COMPONENT = True | ||||
|  | ||||
| DEVICE_CLASSES = [ | ||||
|     DEVICE_CLASS_BUTTON, | ||||
|     DEVICE_CLASS_EMPTY, | ||||
|     DEVICE_CLASS_DOORBELL, | ||||
|     DEVICE_CLASS_MOTION, | ||||
| ] | ||||
|  | ||||
| event_ns = cg.esphome_ns.namespace("event") | ||||
| Event = event_ns.class_("Event", cg.EntityBase) | ||||
| EventPtr = Event.operator("ptr") | ||||
|  | ||||
| TriggerEventAction = event_ns.class_("TriggerEventAction", automation.Action) | ||||
|  | ||||
| EventTrigger = event_ns.class_("EventTrigger", automation.Trigger.template()) | ||||
|  | ||||
| validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") | ||||
|  | ||||
| EVENT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( | ||||
|     { | ||||
|         cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent), | ||||
|         cv.GenerateID(): cv.declare_id(Event), | ||||
|         cv.Optional(CONF_DEVICE_CLASS): validate_device_class, | ||||
|         cv.Optional(CONF_ON_EVENT): automation.validate_automation( | ||||
|             { | ||||
|                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger), | ||||
|             } | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
|  | ||||
| _UNDEF = object() | ||||
|  | ||||
|  | ||||
| def event_schema( | ||||
|     class_: MockObjClass = _UNDEF, | ||||
|     *, | ||||
|     icon: str = _UNDEF, | ||||
|     entity_category: str = _UNDEF, | ||||
|     device_class: str = _UNDEF, | ||||
| ) -> cv.Schema: | ||||
|     schema = {} | ||||
|  | ||||
|     if class_ is not _UNDEF: | ||||
|         schema[cv.GenerateID()] = cv.declare_id(class_) | ||||
|  | ||||
|     for key, default, validator in [ | ||||
|         (CONF_ICON, icon, cv.icon), | ||||
|         (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category), | ||||
|         (CONF_DEVICE_CLASS, device_class, validate_device_class), | ||||
|     ]: | ||||
|         if default is not _UNDEF: | ||||
|             schema[cv.Optional(key, default=default)] = validator | ||||
|  | ||||
|     return EVENT_SCHEMA.extend(schema) | ||||
|  | ||||
|  | ||||
| async def setup_event_core_(var, config, *, event_types: list[str]): | ||||
|     await setup_entity(var, config) | ||||
|  | ||||
|     for conf in config.get(CONF_ON_EVENT, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation( | ||||
|             trigger, [(cg.std_string, "event_type")], conf | ||||
|         ) | ||||
|  | ||||
|     cg.add(var.set_event_types(event_types)) | ||||
|  | ||||
|     if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: | ||||
|         cg.add(var.set_device_class(device_class)) | ||||
|  | ||||
|     if mqtt_id := config.get(CONF_MQTT_ID): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|  | ||||
| async def register_event(var, config, *, event_types: list[str]): | ||||
|     if not CORE.has_id(config[CONF_ID]): | ||||
|         var = cg.Pvariable(config[CONF_ID], var) | ||||
|     cg.add(cg.App.register_event(var)) | ||||
|     await setup_event_core_(var, config, event_types=event_types) | ||||
|  | ||||
|  | ||||
| async def new_event(config, *, event_types: list[str]): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await register_event(var, config, event_types=event_types) | ||||
|     return var | ||||
|  | ||||
|  | ||||
| TRIGGER_EVENT_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_ID): cv.use_id(Event), | ||||
|         cv.Required(CONF_EVENT_TYPE): cv.templatable(cv.string_strict), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| @automation.register_action("event.trigger", TriggerEventAction, TRIGGER_EVENT_SCHEMA) | ||||
| async def event_fire_to_code(config, action_id, template_arg, args): | ||||
|     var = cg.new_Pvariable(action_id, template_arg) | ||||
|     await cg.register_parented(var, config[CONF_ID]) | ||||
|     templ = await cg.templatable(config[CONF_EVENT_TYPE], args, cg.std_string) | ||||
|     cg.add(var.set_event_type(templ)) | ||||
|     return var | ||||
|  | ||||
|  | ||||
| @coroutine_with_priority(100.0) | ||||
| async def to_code(config): | ||||
|     cg.add_define("USE_EVENT") | ||||
|     cg.add_global(event_ns.using) | ||||
							
								
								
									
										25
									
								
								esphome/components/event/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphome/components/event/automation.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/event/event.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace event { | ||||
|  | ||||
| template<typename... Ts> class TriggerEventAction : public Action<Ts...>, public Parented<Event> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(std::string, event_type) | ||||
|  | ||||
|   void play(Ts... x) override { this->parent_->trigger(this->event_type_.value(x...)); } | ||||
| }; | ||||
|  | ||||
| class EventTrigger : public Trigger<std::string> { | ||||
|  public: | ||||
|   EventTrigger(Event *event) { | ||||
|     event->add_on_event_callback([this](const std::string &event_type) { this->trigger(event_type); }); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| }  // namespace event | ||||
| }  // namespace esphome | ||||
							
								
								
									
										24
									
								
								esphome/components/event/event.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphome/components/event/event.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| #include "event.h" | ||||
|  | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace event { | ||||
|  | ||||
| static const char *const TAG = "event"; | ||||
|  | ||||
| void Event::trigger(const std::string &event_type) { | ||||
|   if (types_.find(event_type) == types_.end()) { | ||||
|     ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str()); | ||||
|     return; | ||||
|   } | ||||
|   ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), event_type.c_str()); | ||||
|   this->event_callback_.call(event_type); | ||||
| } | ||||
|  | ||||
| void Event::add_on_event_callback(std::function<void(const std::string &event_type)> &&callback) { | ||||
|   this->event_callback_.add(std::move(callback)); | ||||
| } | ||||
|  | ||||
| }  // namespace event | ||||
| }  // namespace esphome | ||||
							
								
								
									
										37
									
								
								esphome/components/event/event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								esphome/components/event/event.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <set> | ||||
| #include <string> | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/entity_base.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace event { | ||||
|  | ||||
| #define LOG_EVENT(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()); \ | ||||
|     } \ | ||||
|     if (!(obj)->get_device_class().empty()) { \ | ||||
|       ESP_LOGCONFIG(TAG, "%s  Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ | ||||
|     } \ | ||||
|   } | ||||
|  | ||||
| class Event : public EntityBase, public EntityBase_DeviceClass { | ||||
|  public: | ||||
|   void trigger(const std::string &event_type); | ||||
|   void set_event_types(const std::set<std::string> &event_types) { this->types_ = event_types; } | ||||
|   std::set<std::string> get_event_types() const { return this->types_; } | ||||
|   void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback); | ||||
|  | ||||
|  protected: | ||||
|   CallbackManager<void(const std::string &event_type)> event_callback_; | ||||
|   std::set<std::string> types_; | ||||
| }; | ||||
|  | ||||
| }  // namespace event | ||||
| }  // namespace esphome | ||||
| @@ -38,7 +38,7 @@ CONFIG_SCHEMA = DISPLAY_MENU_BASE_SCHEMA.extend( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(GraphicalDisplayMenu), | ||||
|             cv.Optional(CONF_DISPLAY): cv.use_id(display.DisplayBuffer), | ||||
|             cv.Optional(CONF_DISPLAY): cv.use_id(display.Display), | ||||
|             cv.Required(CONF_FONT): cv.use_id(font.Font), | ||||
|             cv.Optional(CONF_MENU_ITEM_VALUE): cv.templatable(cv.string), | ||||
|             cv.Optional(CONF_FOREGROUND_COLOR): cv.use_id(color.ColorStruct), | ||||
|   | ||||
| @@ -229,7 +229,8 @@ inline void GraphicalDisplayMenu::draw_item(display::Display *display, const dis | ||||
|     label.append(this->menu_item_value_.value(&args)); | ||||
|   } | ||||
|  | ||||
|   display->print(bounds->x, bounds->y, this->font_, foreground_color, display::TextAlign::TOP_LEFT, label.c_str()); | ||||
|   display->print(bounds->x, bounds->y, this->font_, foreground_color, display::TextAlign::TOP_LEFT, label.c_str(), | ||||
|                  ~foreground_color); | ||||
| } | ||||
|  | ||||
| void GraphicalDisplayMenu::draw_item(const display_menu_base::MenuItem *item, const uint8_t row, const bool selected) { | ||||
|   | ||||
| @@ -55,11 +55,13 @@ void InternalTemperatureSensor::update() { | ||||
|   uint32_t raw, result; | ||||
|   result = temp_single_get_current_temperature(&raw); | ||||
|   success = (result == 0); | ||||
| #ifdef USE_LIBRETINY_VARIANT_BK7231T | ||||
| #if defined(USE_LIBRETINY_VARIANT_BK7231N) | ||||
|   temperature = raw * -0.38f + 156.0f; | ||||
| #elif defined(USE_LIBRETINY_VARIANT_BK7231T) | ||||
|   temperature = raw * 0.04f; | ||||
| #else | ||||
| #else   // USE_LIBRETINY_VARIANT | ||||
|   temperature = raw * 0.128f; | ||||
| #endif  // USE_LIBRETINY_VARIANT_BK7231T | ||||
| #endif  // USE_LIBRETINY_VARIANT | ||||
| #endif  // USE_BK72XX | ||||
|   if (success && std::isfinite(temperature)) { | ||||
|     this->publish_state(temperature); | ||||
|   | ||||
| @@ -493,19 +493,16 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) { | ||||
| } | ||||
|  | ||||
| int LD2420Component::send_cmd_from_array(CmdFrameT frame) { | ||||
|   uint32_t start_millis = millis(); | ||||
|   uint8_t error = 0; | ||||
|   uint8_t ack_buffer[64]; | ||||
|   uint8_t cmd_buffer[64]; | ||||
|   uint16_t loop_count; | ||||
|   this->cmd_reply_.ack = false; | ||||
|   if (frame.command != CMD_RESTART) | ||||
|     this->set_cmd_active_(true);  // Restart does not reply, thus no ack state required. | ||||
|   uint8_t retry = 3; | ||||
|   while (retry) { | ||||
|     // TODO setup a dynamic method e.g. millis time count etc. to tune for non ESP32 240Mhz devices | ||||
|     // this is ok for now since the module firmware is changing like the weather atm | ||||
|     frame.length = 0; | ||||
|     loop_count = 1250; | ||||
|     uint16_t frame_data_bytes = frame.data_length + 2;  // Always add two bytes for the cmd size | ||||
|  | ||||
|     memcpy(&cmd_buffer[frame.length], &frame.header, sizeof(frame.header)); | ||||
| @@ -538,12 +535,13 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) { | ||||
|         this->readline_(read(), ack_buffer, sizeof(ack_buffer)); | ||||
|       } | ||||
|       delay_microseconds_safe(1450); | ||||
|       if (loop_count <= 0) { | ||||
|       // Wait on an Rx from the LD2420 for up to 3 1 second loops, otherwise it could trigger a WDT. | ||||
|       if ((millis() - start_millis) > 1000) { | ||||
|         start_millis = millis(); | ||||
|         error = LD2420_ERROR_TIMEOUT; | ||||
|         retry--; | ||||
|         break; | ||||
|       } | ||||
|       loop_count--; | ||||
|     } | ||||
|     if (this->cmd_reply_.ack) | ||||
|       retry = 0; | ||||
|   | ||||
| @@ -1,6 +1,10 @@ | ||||
| import json | ||||
| import logging | ||||
| from os.path import dirname, isfile, join | ||||
| from os.path import ( | ||||
|     dirname, | ||||
|     isfile, | ||||
|     join, | ||||
| ) | ||||
|  | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| @@ -55,15 +59,25 @@ def _detect_variant(value): | ||||
|     component: LibreTinyComponent = CORE.data[KEY_LIBRETINY][KEY_COMPONENT_DATA] | ||||
|     board = value[CONF_BOARD] | ||||
|     # read board-default family if not specified | ||||
|     if CONF_FAMILY not in value: | ||||
|         if board not in component.boards: | ||||
|     if board not in component.boards: | ||||
|         if CONF_FAMILY not in value: | ||||
|             raise cv.Invalid( | ||||
|                 "This board is unknown, please set the family manually. " | ||||
|                 "Also, make sure the chosen chip component is correct.", | ||||
|                 "This board is unknown, if you are sure you want to compile with this board selection, " | ||||
|                 f"override with option '{CONF_FAMILY}'", | ||||
|                 path=[CONF_BOARD], | ||||
|             ) | ||||
|         _LOGGER.warning( | ||||
|             "This board is unknown. Make sure the chosen chip component is correct.", | ||||
|         ) | ||||
|     else: | ||||
|         family = component.boards[board][KEY_FAMILY] | ||||
|         if CONF_FAMILY in value and family != value[CONF_FAMILY]: | ||||
|             raise cv.Invalid( | ||||
|                 f"Option '{CONF_FAMILY}' does not match selected board.", | ||||
|                 path=[CONF_FAMILY], | ||||
|             ) | ||||
|         value = value.copy() | ||||
|         value[CONF_FAMILY] = component.boards[board][KEY_FAMILY] | ||||
|         value[CONF_FAMILY] = family | ||||
|     # read component name matching this family | ||||
|     value[CONF_COMPONENT_ID] = FAMILY_COMPONENT[value[CONF_FAMILY]] | ||||
|     # make sure the chosen component matches the family | ||||
| @@ -72,11 +86,6 @@ def _detect_variant(value): | ||||
|             f"The chosen family doesn't belong to '{component.name}' component. The correct component is '{value[CONF_COMPONENT_ID]}'", | ||||
|             path=[CONF_FAMILY], | ||||
|         ) | ||||
|     # warn anyway if the board wasn't found | ||||
|     if board not in component.boards: | ||||
|         _LOGGER.warning( | ||||
|             "This board is unknown. Make sure the chosen chip component is correct.", | ||||
|         ) | ||||
|     return value | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -337,9 +337,12 @@ LightColorValues LightCall::validate_() { | ||||
| void LightCall::transform_parameters_() { | ||||
|   auto traits = this->parent_->get_traits(); | ||||
|  | ||||
|   // Allow CWWW modes to be set with a white value and/or color temperature. This is used by HA, | ||||
|   // which doesn't support CWWW modes (yet?), and for compatibility with the pre-colormode model, | ||||
|   // as CWWW and RGBWW lights used to represent their values as white + color temperature. | ||||
|   // Allow CWWW modes to be set with a white value and/or color temperature. | ||||
|   // This is used in three cases in HA: | ||||
|   // - CW/WW lights, which set the "brightness" and "color_temperature" | ||||
|   // - RGBWW lights with color_interlock=true, which also sets "brightness" and | ||||
|   //   "color_temperature" (without color_interlock, CW/WW are set directly) | ||||
|   // - Legacy Home Assistant (pre-colormode), which sets "white" and "color_temperature" | ||||
|   if (((this->white_.has_value() && *this->white_ > 0.0f) || this->color_temperature_.has_value()) &&  // | ||||
|       (*this->color_mode_ & ColorCapability::COLD_WARM_WHITE) &&                                       // | ||||
|       !(*this->color_mode_ & ColorCapability::WHITE) &&                                                // | ||||
| @@ -347,21 +350,17 @@ void LightCall::transform_parameters_() { | ||||
|       traits.get_min_mireds() > 0.0f && traits.get_max_mireds() > 0.0f) { | ||||
|     ESP_LOGD(TAG, "'%s' - Setting cold/warm white channels using white/color temperature values.", | ||||
|              this->parent_->get_name().c_str()); | ||||
|     auto current_values = this->parent_->remote_values; | ||||
|     if (this->color_temperature_.has_value()) { | ||||
|       const float white = | ||||
|           this->white_.value_or(fmaxf(current_values.get_cold_white(), current_values.get_warm_white())); | ||||
|       const float color_temp = clamp(*this->color_temperature_, traits.get_min_mireds(), traits.get_max_mireds()); | ||||
|       const float ww_fraction = | ||||
|           (color_temp - traits.get_min_mireds()) / (traits.get_max_mireds() - traits.get_min_mireds()); | ||||
|       const float cw_fraction = 1.0f - ww_fraction; | ||||
|       const float max_cw_ww = std::max(ww_fraction, cw_fraction); | ||||
|       this->cold_white_ = white * gamma_uncorrect(cw_fraction / max_cw_ww, this->parent_->get_gamma_correct()); | ||||
|       this->warm_white_ = white * gamma_uncorrect(ww_fraction / max_cw_ww, this->parent_->get_gamma_correct()); | ||||
|     } else { | ||||
|       const float max_cw_ww = std::max(current_values.get_warm_white(), current_values.get_cold_white()); | ||||
|       this->cold_white_ = *this->white_ * current_values.get_cold_white() / max_cw_ww; | ||||
|       this->warm_white_ = *this->white_ * current_values.get_warm_white() / max_cw_ww; | ||||
|       this->cold_white_ = gamma_uncorrect(cw_fraction / max_cw_ww, this->parent_->get_gamma_correct()); | ||||
|       this->warm_white_ = gamma_uncorrect(ww_fraction / max_cw_ww, this->parent_->get_gamma_correct()); | ||||
|     } | ||||
|     if (this->white_.has_value()) { | ||||
|       this->brightness_ = *this->white_; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -266,6 +266,21 @@ class LightColorValues { | ||||
|   /// Set the color temperature property of these light color values in mired. | ||||
|   void set_color_temperature(float color_temperature) { this->color_temperature_ = color_temperature; } | ||||
|  | ||||
|   /// Get the color temperature property of these light color values in kelvin. | ||||
|   float get_color_temperature_kelvin() const { | ||||
|     if (this->color_temperature_ <= 0) { | ||||
|       return this->color_temperature_; | ||||
|     } | ||||
|     return 1000000.0 / this->color_temperature_; | ||||
|   } | ||||
|   /// Set the color temperature property of these light color values in kelvin. | ||||
|   void set_color_temperature_kelvin(float color_temperature) { | ||||
|     if (color_temperature <= 0) { | ||||
|       return; | ||||
|     } | ||||
|     this->color_temperature_ = 1000000.0 / color_temperature; | ||||
|   } | ||||
|  | ||||
|   /// Get the cold white property of these light color values. In range 0.0 to 1.0. | ||||
|   float get_cold_white() const { return this->cold_white_; } | ||||
|   /// Set the cold white property of these light color values. In range 0.0 to 1.0. | ||||
|   | ||||
| @@ -119,6 +119,8 @@ MQTTTextComponent = mqtt_ns.class_("MQTTTextComponent", MQTTComponent) | ||||
| MQTTSelectComponent = mqtt_ns.class_("MQTTSelectComponent", MQTTComponent) | ||||
| MQTTButtonComponent = mqtt_ns.class_("MQTTButtonComponent", MQTTComponent) | ||||
| MQTTLockComponent = mqtt_ns.class_("MQTTLockComponent", MQTTComponent) | ||||
| MQTTEventComponent = mqtt_ns.class_("MQTTEventComponent", MQTTComponent) | ||||
| MQTTValveComponent = mqtt_ns.class_("MQTTValveComponent", MQTTComponent) | ||||
|  | ||||
| MQTTDiscoveryUniqueIdGenerator = mqtt_ns.enum("MQTTDiscoveryUniqueIdGenerator") | ||||
| MQTT_DISCOVERY_UNIQUE_ID_GENERATOR_OPTIONS = { | ||||
|   | ||||
| @@ -9,8 +9,8 @@ namespace mqtt { | ||||
|  | ||||
| #ifdef USE_MQTT_ABBREVIATIONS | ||||
|  | ||||
| constexpr const char *const MQTT_ACTION_TOPIC = "act_t"; | ||||
| constexpr const char *const MQTT_ACTION_TEMPLATE = "act_tpl"; | ||||
| constexpr const char *const MQTT_ACTION_TOPIC = "act_t"; | ||||
| constexpr const char *const MQTT_AUTOMATION_TYPE = "atype"; | ||||
| constexpr const char *const MQTT_AUX_COMMAND_TOPIC = "aux_cmd_t"; | ||||
| constexpr const char *const MQTT_AUX_STATE_TEMPLATE = "aux_stat_tpl"; | ||||
| @@ -21,60 +21,70 @@ constexpr const char *const MQTT_AVAILABILITY_TOPIC = "avty_t"; | ||||
| constexpr const char *const MQTT_AWAY_MODE_COMMAND_TOPIC = "away_mode_cmd_t"; | ||||
| constexpr const char *const MQTT_AWAY_MODE_STATE_TEMPLATE = "away_mode_stat_tpl"; | ||||
| constexpr const char *const MQTT_AWAY_MODE_STATE_TOPIC = "away_mode_stat_t"; | ||||
| constexpr const char *const MQTT_BATTERY_LEVEL_TEMPLATE = "bat_lev_tpl"; | ||||
| constexpr const char *const MQTT_BATTERY_LEVEL_TOPIC = "bat_lev_t"; | ||||
| constexpr const char *const MQTT_BLUE_TEMPLATE = "b_tpl"; | ||||
| constexpr const char *const MQTT_BRIGHTNESS_COMMAND_TOPIC = "bri_cmd_t"; | ||||
| constexpr const char *const MQTT_BRIGHTNESS_SCALE = "bri_scl"; | ||||
| constexpr const char *const MQTT_BRIGHTNESS_STATE_TOPIC = "bri_stat_t"; | ||||
| constexpr const char *const MQTT_BRIGHTNESS_TEMPLATE = "bri_tpl"; | ||||
| constexpr const char *const MQTT_BRIGHTNESS_VALUE_TEMPLATE = "bri_val_tpl"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TEMPLATE = "clr_temp_cmd_tpl"; | ||||
| constexpr const char *const MQTT_BATTERY_LEVEL_TOPIC = "bat_lev_t"; | ||||
| constexpr const char *const MQTT_BATTERY_LEVEL_TEMPLATE = "bat_lev_tpl"; | ||||
| constexpr const char *const MQTT_CONFIGURATION_URL = "cu"; | ||||
| constexpr const char *const MQTT_CHARGING_TOPIC = "chrg_t"; | ||||
| constexpr const char *const MQTT_CHARGING_TEMPLATE = "chrg_tpl"; | ||||
| constexpr const char *const MQTT_CHARGING_TOPIC = "chrg_t"; | ||||
| constexpr const char *const MQTT_CLEANING_TEMPLATE = "cln_tpl"; | ||||
| constexpr const char *const MQTT_CLEANING_TOPIC = "cln_t"; | ||||
| constexpr const char *const MQTT_CODE_ARM_REQUIRED = "cod_arm_req"; | ||||
| constexpr const char *const MQTT_CODE_DISARM_REQUIRED = "cod_dis_req"; | ||||
| constexpr const char *const MQTT_COLOR_MODE = "clrm"; | ||||
| constexpr const char *const MQTT_COLOR_MODE_STATE_TOPIC = "clrm_stat_t"; | ||||
| constexpr const char *const MQTT_COLOR_MODE_VALUE_TEMPLATE = "clrm_val_tpl"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TEMPLATE = "clr_temp_cmd_tpl"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TOPIC = "clr_temp_cmd_t"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_STATE_TOPIC = "clr_temp_stat_t"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_TEMPLATE = "clr_temp_tpl"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_VALUE_TEMPLATE = "clr_temp_val_tpl"; | ||||
| constexpr const char *const MQTT_CLEANING_TOPIC = "cln_t"; | ||||
| constexpr const char *const MQTT_CLEANING_TEMPLATE = "cln_tpl"; | ||||
| constexpr const char *const MQTT_COMMAND_OFF_TEMPLATE = "cmd_off_tpl"; | ||||
| constexpr const char *const MQTT_COMMAND_ON_TEMPLATE = "cmd_on_tpl"; | ||||
| constexpr const char *const MQTT_COMMAND_TOPIC = "cmd_t"; | ||||
| constexpr const char *const MQTT_COMMAND_RETAIN = "ret"; | ||||
| constexpr const char *const MQTT_COMMAND_TEMPLATE = "cmd_tpl"; | ||||
| constexpr const char *const MQTT_CODE_ARM_REQUIRED = "cod_arm_req"; | ||||
| constexpr const char *const MQTT_CODE_DISARM_REQUIRED = "cod_dis_req"; | ||||
| constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "curr_temp_t"; | ||||
| constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "curr_temp_tpl"; | ||||
| constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "curr_hum_t"; | ||||
| constexpr const char *const MQTT_COMMAND_TOPIC = "cmd_t"; | ||||
| constexpr const char *const MQTT_CONFIGURATION_URL = "cu"; | ||||
| constexpr const char *const MQTT_CURRENT_HUMIDITY_TEMPLATE = "curr_hum_tpl"; | ||||
| constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "curr_hum_t"; | ||||
| constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "curr_temp_tpl"; | ||||
| constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "curr_temp_t"; | ||||
| constexpr const char *const MQTT_DEVICE = "dev"; | ||||
| constexpr const char *const MQTT_DEVICE_CLASS = "dev_cla"; | ||||
| constexpr const char *const MQTT_DOCKED_TOPIC = "dock_t"; | ||||
| constexpr const char *const MQTT_DEVICE_CONNECTIONS = "cns"; | ||||
| constexpr const char *const MQTT_DEVICE_IDENTIFIERS = "ids"; | ||||
| constexpr const char *const MQTT_DEVICE_MANUFACTURER = "mf"; | ||||
| constexpr const char *const MQTT_DEVICE_MODEL = "mdl"; | ||||
| constexpr const char *const MQTT_DEVICE_NAME = "name"; | ||||
| constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "sa"; | ||||
| constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw"; | ||||
| constexpr const char *const MQTT_DOCKED_TEMPLATE = "dock_tpl"; | ||||
| constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "en"; | ||||
| constexpr const char *const MQTT_ERROR_TOPIC = "err_t"; | ||||
| constexpr const char *const MQTT_ERROR_TEMPLATE = "err_tpl"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_TOPIC = "fanspd_t"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_TEMPLATE = "fanspd_tpl"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_LIST = "fanspd_lst"; | ||||
| constexpr const char *const MQTT_FLASH_TIME_LONG = "flsh_tlng"; | ||||
| constexpr const char *const MQTT_FLASH_TIME_SHORT = "flsh_tsht"; | ||||
| constexpr const char *const MQTT_DOCKED_TOPIC = "dock_t"; | ||||
| constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "fx_cmd_t"; | ||||
| constexpr const char *const MQTT_EFFECT_LIST = "fx_list"; | ||||
| constexpr const char *const MQTT_EFFECT_STATE_TOPIC = "fx_stat_t"; | ||||
| constexpr const char *const MQTT_EFFECT_TEMPLATE = "fx_tpl"; | ||||
| constexpr const char *const MQTT_EFFECT_VALUE_TEMPLATE = "fx_val_tpl"; | ||||
| constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "en"; | ||||
| constexpr const char *const MQTT_ENTITY_CATEGORY = "ent_cat"; | ||||
| constexpr const char *const MQTT_ERROR_TEMPLATE = "err_tpl"; | ||||
| constexpr const char *const MQTT_ERROR_TOPIC = "err_t"; | ||||
| constexpr const char *const MQTT_EVENT_TYPE = "event_type"; | ||||
| constexpr const char *const MQTT_EVENT_TYPES = "evt_typ"; | ||||
| constexpr const char *const MQTT_EXPIRE_AFTER = "exp_aft"; | ||||
| constexpr const char *const MQTT_FAN_MODE_COMMAND_TEMPLATE = "fan_mode_cmd_tpl"; | ||||
| constexpr const char *const MQTT_FAN_MODE_COMMAND_TOPIC = "fan_mode_cmd_t"; | ||||
| constexpr const char *const MQTT_FAN_MODE_STATE_TEMPLATE = "fan_mode_stat_tpl"; | ||||
| constexpr const char *const MQTT_FAN_MODE_STATE_TOPIC = "fan_mode_stat_t"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_LIST = "fanspd_lst"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_TEMPLATE = "fanspd_tpl"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_TOPIC = "fanspd_t"; | ||||
| constexpr const char *const MQTT_FLASH_TIME_LONG = "flsh_tlng"; | ||||
| constexpr const char *const MQTT_FLASH_TIME_SHORT = "flsh_tsht"; | ||||
| constexpr const char *const MQTT_FORCE_UPDATE = "frc_upd"; | ||||
| constexpr const char *const MQTT_GREEN_TEMPLATE = "g_tpl"; | ||||
| constexpr const char *const MQTT_HOLD_COMMAND_TEMPLATE = "hold_cmd_tpl"; | ||||
| @@ -86,56 +96,49 @@ constexpr const char *const MQTT_HS_STATE_TOPIC = "hs_stat_t"; | ||||
| constexpr const char *const MQTT_HS_VALUE_TEMPLATE = "hs_val_tpl"; | ||||
| constexpr const char *const MQTT_ICON = "ic"; | ||||
| constexpr const char *const MQTT_INITIAL = "init"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "hum_cmd_t"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "hum_cmd_tpl"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "hum_stat_t"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "hum_state_tpl"; | ||||
| constexpr const char *const MQTT_JSON_ATTRIBUTES = "json_attr"; | ||||
| constexpr const char *const MQTT_JSON_ATTRIBUTES_TOPIC = "json_attr_t"; | ||||
| constexpr const char *const MQTT_JSON_ATTRIBUTES_TEMPLATE = "json_attr_tpl"; | ||||
| constexpr const char *const MQTT_JSON_ATTRIBUTES_TOPIC = "json_attr_t"; | ||||
| constexpr const char *const MQTT_LAST_RESET_TOPIC = "lrst_t"; | ||||
| constexpr const char *const MQTT_LAST_RESET_VALUE_TEMPLATE = "lrst_val_tpl"; | ||||
| constexpr const char *const MQTT_MAX = "max"; | ||||
| constexpr const char *const MQTT_MIN = "min"; | ||||
| constexpr const char *const MQTT_MAX_HUMIDITY = "max_hum"; | ||||
| constexpr const char *const MQTT_MIN_HUMIDITY = "min_hum"; | ||||
| constexpr const char *const MQTT_MAX_MIREDS = "max_mirs"; | ||||
| constexpr const char *const MQTT_MIN_MIREDS = "min_mirs"; | ||||
| constexpr const char *const MQTT_MAX_TEMP = "max_temp"; | ||||
| constexpr const char *const MQTT_MIN = "min"; | ||||
| constexpr const char *const MQTT_MIN_HUMIDITY = "min_hum"; | ||||
| constexpr const char *const MQTT_MIN_MIREDS = "min_mirs"; | ||||
| constexpr const char *const MQTT_MIN_TEMP = "min_temp"; | ||||
| constexpr const char *const MQTT_MODE = "mode"; | ||||
| constexpr const char *const MQTT_MODE_COMMAND_TEMPLATE = "mode_cmd_tpl"; | ||||
| constexpr const char *const MQTT_MODE_COMMAND_TOPIC = "mode_cmd_t"; | ||||
| constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_stat_t"; | ||||
| constexpr const char *const MQTT_MODE_STATE_TEMPLATE = "mode_stat_tpl"; | ||||
| constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_stat_t"; | ||||
| constexpr const char *const MQTT_MODES = "modes"; | ||||
| constexpr const char *const MQTT_NAME = "name"; | ||||
| constexpr const char *const MQTT_OBJECT_ID = "obj_id"; | ||||
| constexpr const char *const MQTT_OFF_DELAY = "off_dly"; | ||||
| constexpr const char *const MQTT_ON_COMMAND_TYPE = "on_cmd_type"; | ||||
| constexpr const char *const MQTT_OPTIONS = "ops"; | ||||
| constexpr const char *const MQTT_OPTIMISTIC = "opt"; | ||||
| constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC = "osc_cmd_t"; | ||||
| constexpr const char *const MQTT_OPTIONS = "ops"; | ||||
| constexpr const char *const MQTT_OSCILLATION_COMMAND_TEMPLATE = "osc_cmd_tpl"; | ||||
| constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC = "osc_cmd_t"; | ||||
| constexpr const char *const MQTT_OSCILLATION_STATE_TOPIC = "osc_stat_t"; | ||||
| constexpr const char *const MQTT_OSCILLATION_VALUE_TEMPLATE = "osc_val_tpl"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_COMMAND_TOPIC = "pct_cmd_t"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_COMMAND_TEMPLATE = "pct_cmd_tpl"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_STATE_TOPIC = "pct_stat_t"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_VALUE_TEMPLATE = "pct_val_tpl"; | ||||
| constexpr const char *const MQTT_PAYLOAD = "pl"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_AWAY = "pl_arm_away"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_CUSTOM_BYPASS = "pl_arm_custom_b"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_HOME = "pl_arm_home"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_NIGHT = "pl_arm_nite"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_VACATION = "pl_arm_vacation"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_CUSTOM_BYPASS = "pl_arm_custom_b"; | ||||
| constexpr const char *const MQTT_PAYLOAD_AVAILABLE = "pl_avail"; | ||||
| constexpr const char *const MQTT_PAYLOAD_CLEAN_SPOT = "pl_cln_sp"; | ||||
| constexpr const char *const MQTT_PAYLOAD_CLOSE = "pl_cls"; | ||||
| constexpr const char *const MQTT_PAYLOAD_DISARM = "pl_disarm"; | ||||
| constexpr const char *const MQTT_PAYLOAD_HIGH_SPEED = "pl_hi_spd"; | ||||
| constexpr const char *const MQTT_PAYLOAD_HOME = "pl_home"; | ||||
| constexpr const char *const MQTT_PAYLOAD_LOCK = "pl_lock"; | ||||
| constexpr const char *const MQTT_PAYLOAD_LOCATE = "pl_loc"; | ||||
| constexpr const char *const MQTT_PAYLOAD_LOCK = "pl_lock"; | ||||
| constexpr const char *const MQTT_PAYLOAD_LOW_SPEED = "pl_lo_spd"; | ||||
| constexpr const char *const MQTT_PAYLOAD_MEDIUM_SPEED = "pl_med_spd"; | ||||
| constexpr const char *const MQTT_PAYLOAD_NOT_AVAILABLE = "pl_not_avail"; | ||||
| @@ -152,20 +155,26 @@ constexpr const char *const MQTT_PAYLOAD_RESET_HUMIDITY = "pl_rst_hum"; | ||||
| constexpr const char *const MQTT_PAYLOAD_RESET_MODE = "pl_rst_mode"; | ||||
| constexpr const char *const MQTT_PAYLOAD_RESET_PERCENTAGE = "pl_rst_pct"; | ||||
| constexpr const char *const MQTT_PAYLOAD_RESET_PRESET_MODE = "pl_rst_pr_mode"; | ||||
| constexpr const char *const MQTT_PAYLOAD_STOP = "pl_stop"; | ||||
| constexpr const char *const MQTT_PAYLOAD_RETURN_TO_BASE = "pl_ret"; | ||||
| constexpr const char *const MQTT_PAYLOAD_START = "pl_strt"; | ||||
| constexpr const char *const MQTT_PAYLOAD_START_PAUSE = "pl_stpa"; | ||||
| constexpr const char *const MQTT_PAYLOAD_RETURN_TO_BASE = "pl_ret"; | ||||
| constexpr const char *const MQTT_PAYLOAD_STOP = "pl_stop"; | ||||
| constexpr const char *const MQTT_PAYLOAD_TURN_OFF = "pl_toff"; | ||||
| constexpr const char *const MQTT_PAYLOAD_TURN_ON = "pl_ton"; | ||||
| constexpr const char *const MQTT_PAYLOAD_UNLOCK = "pl_unlk"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_COMMAND_TEMPLATE = "pct_cmd_tpl"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_COMMAND_TOPIC = "pct_cmd_t"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_STATE_TOPIC = "pct_stat_t"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_VALUE_TEMPLATE = "pct_val_tpl"; | ||||
| constexpr const char *const MQTT_POSITION_CLOSED = "pos_clsd"; | ||||
| constexpr const char *const MQTT_POSITION_OPEN = "pos_open"; | ||||
| constexpr const char *const MQTT_POSITION_TEMPLATE = "pos_tpl"; | ||||
| constexpr const char *const MQTT_POSITION_TOPIC = "pos_t"; | ||||
| constexpr const char *const MQTT_POWER_COMMAND_TOPIC = "pow_cmd_t"; | ||||
| constexpr const char *const MQTT_POWER_STATE_TOPIC = "pow_stat_t"; | ||||
| constexpr const char *const MQTT_POWER_STATE_TEMPLATE = "pow_stat_tpl"; | ||||
| constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "pr_mode_cmd_t"; | ||||
| constexpr const char *const MQTT_POWER_STATE_TOPIC = "pow_stat_t"; | ||||
| constexpr const char *const MQTT_PRESET_MODE_COMMAND_TEMPLATE = "pr_mode_cmd_tpl"; | ||||
| constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "pr_mode_cmd_t"; | ||||
| constexpr const char *const MQTT_PRESET_MODE_STATE_TOPIC = "pr_mode_stat_t"; | ||||
| constexpr const char *const MQTT_PRESET_MODE_VALUE_TEMPLATE = "pr_mode_val_tpl"; | ||||
| constexpr const char *const MQTT_PRESET_MODES = "pr_modes"; | ||||
| @@ -188,36 +197,38 @@ constexpr const char *const MQTT_SEND_IF_OFF = "send_if_off"; | ||||
| constexpr const char *const MQTT_SET_FAN_SPEED_TOPIC = "set_fan_spd_t"; | ||||
| constexpr const char *const MQTT_SET_POSITION_TEMPLATE = "set_pos_tpl"; | ||||
| constexpr const char *const MQTT_SET_POSITION_TOPIC = "set_pos_t"; | ||||
| constexpr const char *const MQTT_POSITION_TOPIC = "pos_t"; | ||||
| constexpr const char *const MQTT_POSITION_TEMPLATE = "pos_tpl"; | ||||
| constexpr const char *const MQTT_SOURCE_TYPE = "src_type"; | ||||
| constexpr const char *const MQTT_SPEED_COMMAND_TOPIC = "spd_cmd_t"; | ||||
| constexpr const char *const MQTT_SPEED_STATE_TOPIC = "spd_stat_t"; | ||||
| constexpr const char *const MQTT_SPEED_RANGE_MIN = "spd_rng_min"; | ||||
| constexpr const char *const MQTT_SPEED_RANGE_MAX = "spd_rng_max"; | ||||
| constexpr const char *const MQTT_SPEED_RANGE_MIN = "spd_rng_min"; | ||||
| constexpr const char *const MQTT_SPEED_STATE_TOPIC = "spd_stat_t"; | ||||
| constexpr const char *const MQTT_SPEED_VALUE_TEMPLATE = "spd_val_tpl"; | ||||
| constexpr const char *const MQTT_SPEEDS = "spds"; | ||||
| constexpr const char *const MQTT_SOURCE_TYPE = "src_type"; | ||||
| constexpr const char *const MQTT_STATE_CLASS = "stat_cla"; | ||||
| constexpr const char *const MQTT_STATE_CLOSED = "stat_clsd"; | ||||
| constexpr const char *const MQTT_STATE_CLOSING = "stat_closing"; | ||||
| constexpr const char *const MQTT_STATE_LOCKED = "stat_locked"; | ||||
| constexpr const char *const MQTT_STATE_OFF = "stat_off"; | ||||
| constexpr const char *const MQTT_STATE_ON = "stat_on"; | ||||
| constexpr const char *const MQTT_STATE_OPEN = "stat_open"; | ||||
| constexpr const char *const MQTT_STATE_OPENING = "stat_opening"; | ||||
| constexpr const char *const MQTT_STATE_STOPPED = "stat_stopped"; | ||||
| constexpr const char *const MQTT_STATE_LOCKED = "stat_locked"; | ||||
| constexpr const char *const MQTT_STATE_UNLOCKED = "stat_unlocked"; | ||||
| constexpr const char *const MQTT_STATE_TOPIC = "stat_t"; | ||||
| constexpr const char *const MQTT_STATE_TEMPLATE = "stat_tpl"; | ||||
| constexpr const char *const MQTT_STATE_TOPIC = "stat_t"; | ||||
| constexpr const char *const MQTT_STATE_UNLOCKED = "stat_unlocked"; | ||||
| constexpr const char *const MQTT_STATE_VALUE_TEMPLATE = "stat_val_tpl"; | ||||
| constexpr const char *const MQTT_STEP = "step"; | ||||
| constexpr const char *const MQTT_SUBTYPE = "stype"; | ||||
| constexpr const char *const MQTT_SUPPORTED_FEATURES = "sup_feat"; | ||||
| constexpr const char *const MQTT_SUPPORTED_COLOR_MODES = "sup_clrm"; | ||||
| constexpr const char *const MQTT_SUPPORTED_FEATURES = "sup_feat"; | ||||
| constexpr const char *const MQTT_SWING_MODE_COMMAND_TEMPLATE = "swing_mode_cmd_tpl"; | ||||
| constexpr const char *const MQTT_SWING_MODE_COMMAND_TOPIC = "swing_mode_cmd_t"; | ||||
| constexpr const char *const MQTT_SWING_MODE_STATE_TEMPLATE = "swing_mode_stat_tpl"; | ||||
| constexpr const char *const MQTT_SWING_MODE_STATE_TOPIC = "swing_mode_stat_t"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "hum_cmd_tpl"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "hum_cmd_t"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "hum_state_tpl"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "hum_stat_t"; | ||||
| constexpr const char *const MQTT_TEMPERATURE_COMMAND_TEMPLATE = "temp_cmd_tpl"; | ||||
| constexpr const char *const MQTT_TEMPERATURE_COMMAND_TOPIC = "temp_cmd_t"; | ||||
| constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE = "temp_hi_cmd_tpl"; | ||||
| @@ -232,15 +243,15 @@ constexpr const char *const MQTT_TEMPERATURE_STATE_TEMPLATE = "temp_stat_tpl"; | ||||
| constexpr const char *const MQTT_TEMPERATURE_STATE_TOPIC = "temp_stat_t"; | ||||
| constexpr const char *const MQTT_TEMPERATURE_UNIT = "temp_unit"; | ||||
| constexpr const char *const MQTT_TILT_CLOSED_VALUE = "tilt_clsd_val"; | ||||
| constexpr const char *const MQTT_TILT_COMMAND_TOPIC = "tilt_cmd_t"; | ||||
| constexpr const char *const MQTT_TILT_COMMAND_TEMPLATE = "tilt_cmd_tpl"; | ||||
| constexpr const char *const MQTT_TILT_COMMAND_TOPIC = "tilt_cmd_t"; | ||||
| constexpr const char *const MQTT_TILT_INVERT_STATE = "tilt_inv_stat"; | ||||
| constexpr const char *const MQTT_TILT_MAX = "tilt_max"; | ||||
| constexpr const char *const MQTT_TILT_MIN = "tilt_min"; | ||||
| constexpr const char *const MQTT_TILT_OPENED_VALUE = "tilt_opnd_val"; | ||||
| constexpr const char *const MQTT_TILT_OPTIMISTIC = "tilt_opt"; | ||||
| constexpr const char *const MQTT_TILT_STATUS_TOPIC = "tilt_status_t"; | ||||
| constexpr const char *const MQTT_TILT_STATUS_TEMPLATE = "tilt_status_tpl"; | ||||
| constexpr const char *const MQTT_TILT_STATUS_TOPIC = "tilt_status_t"; | ||||
| constexpr const char *const MQTT_TOPIC = "t"; | ||||
| constexpr const char *const MQTT_UNIQUE_ID = "uniq_id"; | ||||
| constexpr const char *const MQTT_UNIT_OF_MEASUREMENT = "unit_of_meas"; | ||||
| @@ -255,18 +266,10 @@ constexpr const char *const MQTT_XY_COMMAND_TOPIC = "xy_cmd_t"; | ||||
| constexpr const char *const MQTT_XY_STATE_TOPIC = "xy_stat_t"; | ||||
| constexpr const char *const MQTT_XY_VALUE_TEMPLATE = "xy_val_tpl"; | ||||
|  | ||||
| constexpr const char *const MQTT_DEVICE_CONNECTIONS = "cns"; | ||||
| constexpr const char *const MQTT_DEVICE_IDENTIFIERS = "ids"; | ||||
| constexpr const char *const MQTT_DEVICE_NAME = "name"; | ||||
| constexpr const char *const MQTT_DEVICE_MANUFACTURER = "mf"; | ||||
| constexpr const char *const MQTT_DEVICE_MODEL = "mdl"; | ||||
| constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw"; | ||||
| constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "sa"; | ||||
|  | ||||
| #else | ||||
|  | ||||
| constexpr const char *const MQTT_ACTION_TOPIC = "action_topic"; | ||||
| constexpr const char *const MQTT_ACTION_TEMPLATE = "action_template"; | ||||
| constexpr const char *const MQTT_ACTION_TOPIC = "action_topic"; | ||||
| constexpr const char *const MQTT_AUTOMATION_TYPE = "automation_type"; | ||||
| constexpr const char *const MQTT_AUX_COMMAND_TOPIC = "aux_command_topic"; | ||||
| constexpr const char *const MQTT_AUX_STATE_TEMPLATE = "aux_state_template"; | ||||
| @@ -277,60 +280,70 @@ constexpr const char *const MQTT_AVAILABILITY_TOPIC = "availability_topic"; | ||||
| constexpr const char *const MQTT_AWAY_MODE_COMMAND_TOPIC = "away_mode_command_topic"; | ||||
| constexpr const char *const MQTT_AWAY_MODE_STATE_TEMPLATE = "away_mode_state_template"; | ||||
| constexpr const char *const MQTT_AWAY_MODE_STATE_TOPIC = "away_mode_state_topic"; | ||||
| constexpr const char *const MQTT_BATTERY_LEVEL_TEMPLATE = "battery_level_template"; | ||||
| constexpr const char *const MQTT_BATTERY_LEVEL_TOPIC = "battery_level_topic"; | ||||
| constexpr const char *const MQTT_BLUE_TEMPLATE = "blue_template"; | ||||
| constexpr const char *const MQTT_BRIGHTNESS_COMMAND_TOPIC = "brightness_command_topic"; | ||||
| constexpr const char *const MQTT_BRIGHTNESS_SCALE = "brightness_scale"; | ||||
| constexpr const char *const MQTT_BRIGHTNESS_STATE_TOPIC = "brightness_state_topic"; | ||||
| constexpr const char *const MQTT_BRIGHTNESS_TEMPLATE = "brightness_template"; | ||||
| constexpr const char *const MQTT_BRIGHTNESS_VALUE_TEMPLATE = "brightness_value_template"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TEMPLATE = "color_temp_command_template"; | ||||
| constexpr const char *const MQTT_BATTERY_LEVEL_TOPIC = "battery_level_topic"; | ||||
| constexpr const char *const MQTT_BATTERY_LEVEL_TEMPLATE = "battery_level_template"; | ||||
| constexpr const char *const MQTT_CONFIGURATION_URL = "configuration_url"; | ||||
| constexpr const char *const MQTT_CHARGING_TOPIC = "charging_topic"; | ||||
| constexpr const char *const MQTT_CHARGING_TEMPLATE = "charging_template"; | ||||
| constexpr const char *const MQTT_CHARGING_TOPIC = "charging_topic"; | ||||
| constexpr const char *const MQTT_CLEANING_TEMPLATE = "cleaning_template"; | ||||
| constexpr const char *const MQTT_CLEANING_TOPIC = "cleaning_topic"; | ||||
| constexpr const char *const MQTT_CODE_ARM_REQUIRED = "code_arm_required"; | ||||
| constexpr const char *const MQTT_CODE_DISARM_REQUIRED = "code_disarm_required"; | ||||
| constexpr const char *const MQTT_COLOR_MODE = "color_mode"; | ||||
| constexpr const char *const MQTT_COLOR_MODE_STATE_TOPIC = "color_mode_state_topic"; | ||||
| constexpr const char *const MQTT_COLOR_MODE_VALUE_TEMPLATE = "color_mode_value_template"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TEMPLATE = "color_temp_command_template"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_COMMAND_TOPIC = "color_temp_command_topic"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_STATE_TOPIC = "color_temp_state_topic"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_TEMPLATE = "color_temp_template"; | ||||
| constexpr const char *const MQTT_COLOR_TEMP_VALUE_TEMPLATE = "color_temp_value_template"; | ||||
| constexpr const char *const MQTT_CLEANING_TOPIC = "cleaning_topic"; | ||||
| constexpr const char *const MQTT_CLEANING_TEMPLATE = "cleaning_template"; | ||||
| constexpr const char *const MQTT_COMMAND_OFF_TEMPLATE = "command_off_template"; | ||||
| constexpr const char *const MQTT_COMMAND_ON_TEMPLATE = "command_on_template"; | ||||
| constexpr const char *const MQTT_COMMAND_TOPIC = "command_topic"; | ||||
| constexpr const char *const MQTT_COMMAND_RETAIN = "retain"; | ||||
| constexpr const char *const MQTT_COMMAND_TEMPLATE = "command_template"; | ||||
| constexpr const char *const MQTT_CODE_ARM_REQUIRED = "code_arm_required"; | ||||
| constexpr const char *const MQTT_CODE_DISARM_REQUIRED = "code_disarm_required"; | ||||
| constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "current_temperature_topic"; | ||||
| constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "current_temperature_template"; | ||||
| constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "current_humidity_topic"; | ||||
| constexpr const char *const MQTT_COMMAND_TOPIC = "command_topic"; | ||||
| constexpr const char *const MQTT_CONFIGURATION_URL = "configuration_url"; | ||||
| constexpr const char *const MQTT_CURRENT_HUMIDITY_TEMPLATE = "current_humidity_template"; | ||||
| constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "current_humidity_topic"; | ||||
| constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "current_temperature_template"; | ||||
| constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "current_temperature_topic"; | ||||
| constexpr const char *const MQTT_DEVICE = "device"; | ||||
| constexpr const char *const MQTT_DEVICE_CLASS = "device_class"; | ||||
| constexpr const char *const MQTT_DOCKED_TOPIC = "docked_topic"; | ||||
| constexpr const char *const MQTT_DEVICE_CONNECTIONS = "connections"; | ||||
| constexpr const char *const MQTT_DEVICE_IDENTIFIERS = "identifiers"; | ||||
| constexpr const char *const MQTT_DEVICE_MANUFACTURER = "manufacturer"; | ||||
| constexpr const char *const MQTT_DEVICE_MODEL = "model"; | ||||
| constexpr const char *const MQTT_DEVICE_NAME = "name"; | ||||
| constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "suggested_area"; | ||||
| constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw_version"; | ||||
| constexpr const char *const MQTT_DOCKED_TEMPLATE = "docked_template"; | ||||
| constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "enabled_by_default"; | ||||
| constexpr const char *const MQTT_ERROR_TOPIC = "error_topic"; | ||||
| constexpr const char *const MQTT_ERROR_TEMPLATE = "error_template"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_TOPIC = "fan_speed_topic"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_TEMPLATE = "fan_speed_template"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_LIST = "fan_speed_list"; | ||||
| constexpr const char *const MQTT_FLASH_TIME_LONG = "flash_time_long"; | ||||
| constexpr const char *const MQTT_FLASH_TIME_SHORT = "flash_time_short"; | ||||
| constexpr const char *const MQTT_DOCKED_TOPIC = "docked_topic"; | ||||
| constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "effect_command_topic"; | ||||
| constexpr const char *const MQTT_EFFECT_LIST = "effect_list"; | ||||
| constexpr const char *const MQTT_EFFECT_STATE_TOPIC = "effect_state_topic"; | ||||
| constexpr const char *const MQTT_EFFECT_TEMPLATE = "effect_template"; | ||||
| constexpr const char *const MQTT_EFFECT_VALUE_TEMPLATE = "effect_value_template"; | ||||
| constexpr const char *const MQTT_ENABLED_BY_DEFAULT = "enabled_by_default"; | ||||
| constexpr const char *const MQTT_ENTITY_CATEGORY = "entity_category"; | ||||
| constexpr const char *const MQTT_ERROR_TEMPLATE = "error_template"; | ||||
| constexpr const char *const MQTT_ERROR_TOPIC = "error_topic"; | ||||
| constexpr const char *const MQTT_EVENT_TYPE = "event_type"; | ||||
| constexpr const char *const MQTT_EVENT_TYPES = "event_types"; | ||||
| constexpr const char *const MQTT_EXPIRE_AFTER = "expire_after"; | ||||
| constexpr const char *const MQTT_FAN_MODE_COMMAND_TEMPLATE = "fan_mode_command_template"; | ||||
| constexpr const char *const MQTT_FAN_MODE_COMMAND_TOPIC = "fan_mode_command_topic"; | ||||
| constexpr const char *const MQTT_FAN_MODE_STATE_TEMPLATE = "fan_mode_state_template"; | ||||
| constexpr const char *const MQTT_FAN_MODE_STATE_TOPIC = "fan_mode_state_topic"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_LIST = "fan_speed_list"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_TEMPLATE = "fan_speed_template"; | ||||
| constexpr const char *const MQTT_FAN_SPEED_TOPIC = "fan_speed_topic"; | ||||
| constexpr const char *const MQTT_FLASH_TIME_LONG = "flash_time_long"; | ||||
| constexpr const char *const MQTT_FLASH_TIME_SHORT = "flash_time_short"; | ||||
| constexpr const char *const MQTT_FORCE_UPDATE = "force_update"; | ||||
| constexpr const char *const MQTT_GREEN_TEMPLATE = "green_template"; | ||||
| constexpr const char *const MQTT_HOLD_COMMAND_TEMPLATE = "hold_command_template"; | ||||
| @@ -342,56 +355,49 @@ constexpr const char *const MQTT_HS_STATE_TOPIC = "hs_state_topic"; | ||||
| constexpr const char *const MQTT_HS_VALUE_TEMPLATE = "hs_value_template"; | ||||
| constexpr const char *const MQTT_ICON = "icon"; | ||||
| constexpr const char *const MQTT_INITIAL = "initial"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "target_humidity_command_topic"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "target_humidity_command_template"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "target_humidity_state_topic"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "target_humidity_state_template"; | ||||
| constexpr const char *const MQTT_JSON_ATTRIBUTES = "json_attributes"; | ||||
| constexpr const char *const MQTT_JSON_ATTRIBUTES_TOPIC = "json_attributes_topic"; | ||||
| constexpr const char *const MQTT_JSON_ATTRIBUTES_TEMPLATE = "json_attributes_template"; | ||||
| constexpr const char *const MQTT_JSON_ATTRIBUTES_TOPIC = "json_attributes_topic"; | ||||
| constexpr const char *const MQTT_LAST_RESET_TOPIC = "last_reset_topic"; | ||||
| constexpr const char *const MQTT_LAST_RESET_VALUE_TEMPLATE = "last_reset_value_template"; | ||||
| constexpr const char *const MQTT_MAX = "max"; | ||||
| constexpr const char *const MQTT_MIN = "min"; | ||||
| constexpr const char *const MQTT_MAX_HUMIDITY = "max_humidity"; | ||||
| constexpr const char *const MQTT_MIN_HUMIDITY = "min_humidity"; | ||||
| constexpr const char *const MQTT_MAX_MIREDS = "max_mireds"; | ||||
| constexpr const char *const MQTT_MIN_MIREDS = "min_mireds"; | ||||
| constexpr const char *const MQTT_MAX_TEMP = "max_temp"; | ||||
| constexpr const char *const MQTT_MIN = "min"; | ||||
| constexpr const char *const MQTT_MIN_HUMIDITY = "min_humidity"; | ||||
| constexpr const char *const MQTT_MIN_MIREDS = "min_mireds"; | ||||
| constexpr const char *const MQTT_MIN_TEMP = "min_temp"; | ||||
| constexpr const char *const MQTT_MODE = "mode"; | ||||
| constexpr const char *const MQTT_MODE_COMMAND_TEMPLATE = "mode_command_template"; | ||||
| constexpr const char *const MQTT_MODE_COMMAND_TOPIC = "mode_command_topic"; | ||||
| constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_state_topic"; | ||||
| constexpr const char *const MQTT_MODE_STATE_TEMPLATE = "mode_state_template"; | ||||
| constexpr const char *const MQTT_MODE_STATE_TOPIC = "mode_state_topic"; | ||||
| constexpr const char *const MQTT_MODES = "modes"; | ||||
| constexpr const char *const MQTT_NAME = "name"; | ||||
| constexpr const char *const MQTT_OBJECT_ID = "object_id"; | ||||
| constexpr const char *const MQTT_OFF_DELAY = "off_delay"; | ||||
| constexpr const char *const MQTT_ON_COMMAND_TYPE = "on_command_type"; | ||||
| constexpr const char *const MQTT_OPTIONS = "options"; | ||||
| constexpr const char *const MQTT_OPTIMISTIC = "optimistic"; | ||||
| constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC = "oscillation_command_topic"; | ||||
| constexpr const char *const MQTT_OPTIONS = "options"; | ||||
| constexpr const char *const MQTT_OSCILLATION_COMMAND_TEMPLATE = "oscillation_command_template"; | ||||
| constexpr const char *const MQTT_OSCILLATION_COMMAND_TOPIC = "oscillation_command_topic"; | ||||
| constexpr const char *const MQTT_OSCILLATION_STATE_TOPIC = "oscillation_state_topic"; | ||||
| constexpr const char *const MQTT_OSCILLATION_VALUE_TEMPLATE = "oscillation_value_template"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_COMMAND_TOPIC = "percentage_command_topic"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_COMMAND_TEMPLATE = "percentage_command_template"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_STATE_TOPIC = "percentage_state_topic"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_VALUE_TEMPLATE = "percentage_value_template"; | ||||
| constexpr const char *const MQTT_PAYLOAD = "payload"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_AWAY = "payload_arm_away"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_CUSTOM_BYPASS = "payload_arm_custom_bypass"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_HOME = "payload_arm_home"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_NIGHT = "payload_arm_night"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_VACATION = "payload_arm_vacation"; | ||||
| constexpr const char *const MQTT_PAYLOAD_ARM_CUSTOM_BYPASS = "payload_arm_custom_bypass"; | ||||
| constexpr const char *const MQTT_PAYLOAD_AVAILABLE = "payload_available"; | ||||
| constexpr const char *const MQTT_PAYLOAD_CLEAN_SPOT = "payload_clean_spot"; | ||||
| constexpr const char *const MQTT_PAYLOAD_CLOSE = "payload_close"; | ||||
| constexpr const char *const MQTT_PAYLOAD_DISARM = "payload_disarm"; | ||||
| constexpr const char *const MQTT_PAYLOAD_HIGH_SPEED = "payload_high_speed"; | ||||
| constexpr const char *const MQTT_PAYLOAD_HOME = "payload_home"; | ||||
| constexpr const char *const MQTT_PAYLOAD_LOCK = "payload_lock"; | ||||
| constexpr const char *const MQTT_PAYLOAD_LOCATE = "payload_locate"; | ||||
| constexpr const char *const MQTT_PAYLOAD_LOCK = "payload_lock"; | ||||
| constexpr const char *const MQTT_PAYLOAD_LOW_SPEED = "payload_low_speed"; | ||||
| constexpr const char *const MQTT_PAYLOAD_MEDIUM_SPEED = "payload_medium_speed"; | ||||
| constexpr const char *const MQTT_PAYLOAD_NOT_AVAILABLE = "payload_not_available"; | ||||
| @@ -408,20 +414,26 @@ constexpr const char *const MQTT_PAYLOAD_RESET_HUMIDITY = "payload_reset_humidit | ||||
| constexpr const char *const MQTT_PAYLOAD_RESET_MODE = "payload_reset_mode"; | ||||
| constexpr const char *const MQTT_PAYLOAD_RESET_PERCENTAGE = "payload_reset_percentage"; | ||||
| constexpr const char *const MQTT_PAYLOAD_RESET_PRESET_MODE = "payload_reset_preset_mode"; | ||||
| constexpr const char *const MQTT_PAYLOAD_STOP = "payload_stop"; | ||||
| constexpr const char *const MQTT_PAYLOAD_RETURN_TO_BASE = "payload_return_to_base"; | ||||
| constexpr const char *const MQTT_PAYLOAD_START = "payload_start"; | ||||
| constexpr const char *const MQTT_PAYLOAD_START_PAUSE = "payload_start_pause"; | ||||
| constexpr const char *const MQTT_PAYLOAD_RETURN_TO_BASE = "payload_return_to_base"; | ||||
| constexpr const char *const MQTT_PAYLOAD_STOP = "payload_stop"; | ||||
| constexpr const char *const MQTT_PAYLOAD_TURN_OFF = "payload_turn_off"; | ||||
| constexpr const char *const MQTT_PAYLOAD_TURN_ON = "payload_turn_on"; | ||||
| constexpr const char *const MQTT_PAYLOAD_UNLOCK = "payload_unlock"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_COMMAND_TEMPLATE = "percentage_command_template"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_COMMAND_TOPIC = "percentage_command_topic"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_STATE_TOPIC = "percentage_state_topic"; | ||||
| constexpr const char *const MQTT_PERCENTAGE_VALUE_TEMPLATE = "percentage_value_template"; | ||||
| constexpr const char *const MQTT_POSITION_CLOSED = "position_closed"; | ||||
| constexpr const char *const MQTT_POSITION_OPEN = "position_open"; | ||||
| constexpr const char *const MQTT_POSITION_TEMPLATE = "position_template"; | ||||
| constexpr const char *const MQTT_POSITION_TOPIC = "position_topic"; | ||||
| constexpr const char *const MQTT_POWER_COMMAND_TOPIC = "power_command_topic"; | ||||
| constexpr const char *const MQTT_POWER_STATE_TOPIC = "power_state_topic"; | ||||
| constexpr const char *const MQTT_POWER_STATE_TEMPLATE = "power_state_template"; | ||||
| constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "preset_mode_command_topic"; | ||||
| constexpr const char *const MQTT_POWER_STATE_TOPIC = "power_state_topic"; | ||||
| constexpr const char *const MQTT_PRESET_MODE_COMMAND_TEMPLATE = "preset_mode_command_template"; | ||||
| constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "preset_mode_command_topic"; | ||||
| constexpr const char *const MQTT_PRESET_MODE_STATE_TOPIC = "preset_mode_state_topic"; | ||||
| constexpr const char *const MQTT_PRESET_MODE_VALUE_TEMPLATE = "preset_mode_value_template"; | ||||
| constexpr const char *const MQTT_PRESET_MODES = "preset_modes"; | ||||
| @@ -444,36 +456,38 @@ constexpr const char *const MQTT_SEND_IF_OFF = "send_if_off"; | ||||
| constexpr const char *const MQTT_SET_FAN_SPEED_TOPIC = "set_fan_speed_topic"; | ||||
| constexpr const char *const MQTT_SET_POSITION_TEMPLATE = "set_position_template"; | ||||
| constexpr const char *const MQTT_SET_POSITION_TOPIC = "set_position_topic"; | ||||
| constexpr const char *const MQTT_POSITION_TOPIC = "position_topic"; | ||||
| constexpr const char *const MQTT_POSITION_TEMPLATE = "position_template"; | ||||
| constexpr const char *const MQTT_SOURCE_TYPE = "source_type"; | ||||
| constexpr const char *const MQTT_SPEED_COMMAND_TOPIC = "speed_command_topic"; | ||||
| constexpr const char *const MQTT_SPEED_STATE_TOPIC = "speed_state_topic"; | ||||
| constexpr const char *const MQTT_SPEED_RANGE_MIN = "speed_range_min"; | ||||
| constexpr const char *const MQTT_SPEED_RANGE_MAX = "speed_range_max"; | ||||
| constexpr const char *const MQTT_SPEED_RANGE_MIN = "speed_range_min"; | ||||
| constexpr const char *const MQTT_SPEED_STATE_TOPIC = "speed_state_topic"; | ||||
| constexpr const char *const MQTT_SPEED_VALUE_TEMPLATE = "speed_value_template"; | ||||
| constexpr const char *const MQTT_SPEEDS = "speeds"; | ||||
| constexpr const char *const MQTT_SOURCE_TYPE = "source_type"; | ||||
| constexpr const char *const MQTT_STATE_CLASS = "state_class"; | ||||
| constexpr const char *const MQTT_STATE_CLOSED = "state_closed"; | ||||
| constexpr const char *const MQTT_STATE_CLOSING = "state_closing"; | ||||
| constexpr const char *const MQTT_STATE_LOCKED = "state_locked"; | ||||
| constexpr const char *const MQTT_STATE_OFF = "state_off"; | ||||
| constexpr const char *const MQTT_STATE_ON = "state_on"; | ||||
| constexpr const char *const MQTT_STATE_OPEN = "state_open"; | ||||
| constexpr const char *const MQTT_STATE_OPENING = "state_opening"; | ||||
| constexpr const char *const MQTT_STATE_STOPPED = "state_stopped"; | ||||
| constexpr const char *const MQTT_STATE_LOCKED = "state_locked"; | ||||
| constexpr const char *const MQTT_STATE_UNLOCKED = "state_unlocked"; | ||||
| constexpr const char *const MQTT_STATE_TOPIC = "state_topic"; | ||||
| constexpr const char *const MQTT_STATE_TEMPLATE = "state_template"; | ||||
| constexpr const char *const MQTT_STATE_TOPIC = "state_topic"; | ||||
| constexpr const char *const MQTT_STATE_UNLOCKED = "state_unlocked"; | ||||
| constexpr const char *const MQTT_STATE_VALUE_TEMPLATE = "state_value_template"; | ||||
| constexpr const char *const MQTT_STEP = "step"; | ||||
| constexpr const char *const MQTT_SUBTYPE = "subtype"; | ||||
| constexpr const char *const MQTT_SUPPORTED_FEATURES = "supported_features"; | ||||
| constexpr const char *const MQTT_SUPPORTED_COLOR_MODES = "supported_color_modes"; | ||||
| constexpr const char *const MQTT_SUPPORTED_FEATURES = "supported_features"; | ||||
| constexpr const char *const MQTT_SWING_MODE_COMMAND_TEMPLATE = "swing_mode_command_template"; | ||||
| constexpr const char *const MQTT_SWING_MODE_COMMAND_TOPIC = "swing_mode_command_topic"; | ||||
| constexpr const char *const MQTT_SWING_MODE_STATE_TEMPLATE = "swing_mode_state_template"; | ||||
| constexpr const char *const MQTT_SWING_MODE_STATE_TOPIC = "swing_mode_state_topic"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "target_humidity_command_template"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "target_humidity_command_topic"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "target_humidity_state_template"; | ||||
| constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "target_humidity_state_topic"; | ||||
| constexpr const char *const MQTT_TEMPERATURE_COMMAND_TEMPLATE = "temperature_command_template"; | ||||
| constexpr const char *const MQTT_TEMPERATURE_COMMAND_TOPIC = "temperature_command_topic"; | ||||
| constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE = "temperature_high_command_template"; | ||||
| @@ -488,15 +502,15 @@ constexpr const char *const MQTT_TEMPERATURE_STATE_TEMPLATE = "temperature_state | ||||
| constexpr const char *const MQTT_TEMPERATURE_STATE_TOPIC = "temperature_state_topic"; | ||||
| constexpr const char *const MQTT_TEMPERATURE_UNIT = "temperature_unit"; | ||||
| constexpr const char *const MQTT_TILT_CLOSED_VALUE = "tilt_closed_value"; | ||||
| constexpr const char *const MQTT_TILT_COMMAND_TOPIC = "tilt_command_topic"; | ||||
| constexpr const char *const MQTT_TILT_COMMAND_TEMPLATE = "tilt_command_template"; | ||||
| constexpr const char *const MQTT_TILT_COMMAND_TOPIC = "tilt_command_topic"; | ||||
| constexpr const char *const MQTT_TILT_INVERT_STATE = "tilt_invert_state"; | ||||
| constexpr const char *const MQTT_TILT_MAX = "tilt_max"; | ||||
| constexpr const char *const MQTT_TILT_MIN = "tilt_min"; | ||||
| constexpr const char *const MQTT_TILT_OPENED_VALUE = "tilt_opened_value"; | ||||
| constexpr const char *const MQTT_TILT_OPTIMISTIC = "tilt_optimistic"; | ||||
| constexpr const char *const MQTT_TILT_STATUS_TOPIC = "tilt_status_topic"; | ||||
| constexpr const char *const MQTT_TILT_STATUS_TEMPLATE = "tilt_status_template"; | ||||
| constexpr const char *const MQTT_TILT_STATUS_TOPIC = "tilt_status_topic"; | ||||
| constexpr const char *const MQTT_TOPIC = "topic"; | ||||
| constexpr const char *const MQTT_UNIQUE_ID = "unique_id"; | ||||
| constexpr const char *const MQTT_UNIT_OF_MEASUREMENT = "unit_of_measurement"; | ||||
| @@ -511,19 +525,8 @@ constexpr const char *const MQTT_XY_COMMAND_TOPIC = "xy_command_topic"; | ||||
| constexpr const char *const MQTT_XY_STATE_TOPIC = "xy_state_topic"; | ||||
| constexpr const char *const MQTT_XY_VALUE_TEMPLATE = "xy_value_template"; | ||||
|  | ||||
| constexpr const char *const MQTT_DEVICE_CONNECTIONS = "connections"; | ||||
| constexpr const char *const MQTT_DEVICE_IDENTIFIERS = "identifiers"; | ||||
| constexpr const char *const MQTT_DEVICE_NAME = "name"; | ||||
| constexpr const char *const MQTT_DEVICE_MANUFACTURER = "manufacturer"; | ||||
| constexpr const char *const MQTT_DEVICE_MODEL = "model"; | ||||
| constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw_version"; | ||||
| constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "suggested_area"; | ||||
| #endif | ||||
|  | ||||
| // Additional MQTT fields where no abbreviation is defined in HA source | ||||
| constexpr const char *const MQTT_ENTITY_CATEGORY = "entity_category"; | ||||
| constexpr const char *const MQTT_MODE = "mode"; | ||||
|  | ||||
| }  // namespace mqtt | ||||
| }  // namespace esphome | ||||
|  | ||||
|   | ||||
							
								
								
									
										54
									
								
								esphome/components/mqtt/mqtt_event.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								esphome/components/mqtt/mqtt_event.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| #include "mqtt_event.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| #include "mqtt_const.h" | ||||
|  | ||||
| #ifdef USE_MQTT | ||||
| #ifdef USE_EVENT | ||||
|  | ||||
| namespace esphome { | ||||
| namespace mqtt { | ||||
|  | ||||
| static const char *const TAG = "mqtt.event"; | ||||
|  | ||||
| using namespace esphome::event; | ||||
|  | ||||
| MQTTEventComponent::MQTTEventComponent(event::Event *event) : event_(event) {} | ||||
|  | ||||
| void MQTTEventComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { | ||||
|   JsonArray event_types = root.createNestedArray(MQTT_EVENT_TYPES); | ||||
|   for (const auto &event_type : this->event_->get_event_types()) | ||||
|     event_types.add(event_type); | ||||
|  | ||||
|   if (!this->event_->get_device_class().empty()) | ||||
|     root[MQTT_DEVICE_CLASS] = this->event_->get_device_class(); | ||||
|  | ||||
|   config.command_topic = false; | ||||
| } | ||||
|  | ||||
| void MQTTEventComponent::setup() { | ||||
|   this->event_->add_on_event_callback([this](const std::string &event_type) { this->publish_event_(event_type); }); | ||||
| } | ||||
|  | ||||
| void MQTTEventComponent::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "MQTT Event '%s': ", this->event_->get_name().c_str()); | ||||
|   ESP_LOGCONFIG(TAG, "Event Types: "); | ||||
|   for (const auto &event_type : this->event_->get_event_types()) { | ||||
|     ESP_LOGCONFIG(TAG, "- %s", event_type.c_str()); | ||||
|   } | ||||
|   LOG_MQTT_COMPONENT(true, true); | ||||
| } | ||||
|  | ||||
| bool MQTTEventComponent::publish_event_(const std::string &event_type) { | ||||
|   return this->publish_json(this->get_state_topic_(), | ||||
|                             [event_type](JsonObject root) { root[MQTT_EVENT_TYPE] = event_type; }); | ||||
| } | ||||
|  | ||||
| std::string MQTTEventComponent::component_type() const { return "event"; } | ||||
| const EntityBase *MQTTEventComponent::get_entity() const { return this->event_; } | ||||
|  | ||||
| }  // namespace mqtt | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif | ||||
| #endif  // USE_MQTT | ||||
							
								
								
									
										39
									
								
								esphome/components/mqtt/mqtt_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								esphome/components/mqtt/mqtt_event.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/defines.h" | ||||
|  | ||||
| #ifdef USE_MQTT | ||||
| #ifdef USE_EVENT | ||||
|  | ||||
| #include "esphome/components/event/event.h" | ||||
| #include "mqtt_component.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace mqtt { | ||||
|  | ||||
| class MQTTEventComponent : public mqtt::MQTTComponent { | ||||
|  public: | ||||
|   explicit MQTTEventComponent(event::Event *event); | ||||
|  | ||||
|   void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override; | ||||
|  | ||||
|   void setup() override; | ||||
|  | ||||
|   void dump_config() override; | ||||
|  | ||||
|   /// Events do not send a state so just return true. | ||||
|   bool send_initial_state() override { return true; } | ||||
|  | ||||
|  protected: | ||||
|   bool publish_event_(const std::string &event_type); | ||||
|   std::string component_type() const override; | ||||
|   const EntityBase *get_entity() const override; | ||||
|  | ||||
|   event::Event *event_; | ||||
| }; | ||||
|  | ||||
| }  // namespace mqtt | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif | ||||
| #endif  // USE_MQTT | ||||
							
								
								
									
										90
									
								
								esphome/components/mqtt/mqtt_valve.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								esphome/components/mqtt/mqtt_valve.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| #include "mqtt_valve.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| #include "mqtt_const.h" | ||||
|  | ||||
| #ifdef USE_MQTT | ||||
| #ifdef USE_VALVE | ||||
|  | ||||
| namespace esphome { | ||||
| namespace mqtt { | ||||
|  | ||||
| static const char *const TAG = "mqtt.valve"; | ||||
|  | ||||
| using namespace esphome::valve; | ||||
|  | ||||
| MQTTValveComponent::MQTTValveComponent(Valve *valve) : valve_(valve) {} | ||||
| void MQTTValveComponent::setup() { | ||||
|   auto traits = this->valve_->get_traits(); | ||||
|   this->valve_->add_on_state_callback([this]() { this->publish_state(); }); | ||||
|   this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) { | ||||
|     auto call = this->valve_->make_call(); | ||||
|     call.set_command(payload.c_str()); | ||||
|     call.perform(); | ||||
|   }); | ||||
|   if (traits.get_supports_position()) { | ||||
|     this->subscribe(this->get_position_command_topic(), [this](const std::string &topic, const std::string &payload) { | ||||
|       auto value = parse_number<float>(payload); | ||||
|       if (!value.has_value()) { | ||||
|         ESP_LOGW(TAG, "Invalid position value: '%s'", payload.c_str()); | ||||
|         return; | ||||
|       } | ||||
|       auto call = this->valve_->make_call(); | ||||
|       call.set_position(*value / 100.0f); | ||||
|       call.perform(); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void MQTTValveComponent::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "MQTT valve '%s':", this->valve_->get_name().c_str()); | ||||
|   auto traits = this->valve_->get_traits(); | ||||
|   bool has_command_topic = traits.get_supports_position(); | ||||
|   LOG_MQTT_COMPONENT(true, has_command_topic) | ||||
|   if (traits.get_supports_position()) { | ||||
|     ESP_LOGCONFIG(TAG, "  Position State Topic: '%s'", this->get_position_state_topic().c_str()); | ||||
|     ESP_LOGCONFIG(TAG, "  Position Command Topic: '%s'", this->get_position_command_topic().c_str()); | ||||
|   } | ||||
| } | ||||
| void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { | ||||
|   if (!this->valve_->get_device_class().empty()) | ||||
|     root[MQTT_DEVICE_CLASS] = this->valve_->get_device_class(); | ||||
|  | ||||
|   auto traits = this->valve_->get_traits(); | ||||
|   if (traits.get_is_assumed_state()) { | ||||
|     root[MQTT_OPTIMISTIC] = true; | ||||
|   } | ||||
|   if (traits.get_supports_position()) { | ||||
|     root[MQTT_POSITION_TOPIC] = this->get_position_state_topic(); | ||||
|     root[MQTT_SET_POSITION_TOPIC] = this->get_position_command_topic(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| std::string MQTTValveComponent::component_type() const { return "valve"; } | ||||
| const EntityBase *MQTTValveComponent::get_entity() const { return this->valve_; } | ||||
|  | ||||
| bool MQTTValveComponent::send_initial_state() { return this->publish_state(); } | ||||
| bool MQTTValveComponent::publish_state() { | ||||
|   auto traits = this->valve_->get_traits(); | ||||
|   bool success = true; | ||||
|   if (traits.get_supports_position()) { | ||||
|     std::string pos = value_accuracy_to_string(roundf(this->valve_->position * 100), 0); | ||||
|     if (!this->publish(this->get_position_state_topic(), pos)) | ||||
|       success = false; | ||||
|   } | ||||
|   const char *state_s = this->valve_->current_operation == VALVE_OPERATION_OPENING   ? "opening" | ||||
|                         : this->valve_->current_operation == VALVE_OPERATION_CLOSING ? "closing" | ||||
|                         : this->valve_->position == VALVE_CLOSED                     ? "closed" | ||||
|                         : this->valve_->position == VALVE_OPEN                       ? "open" | ||||
|                         : traits.get_supports_position()                             ? "open" | ||||
|                                                                                      : "unknown"; | ||||
|   if (!this->publish(this->get_state_topic_(), state_s)) | ||||
|     success = false; | ||||
|   return success; | ||||
| } | ||||
|  | ||||
| }  // namespace mqtt | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif | ||||
| #endif  // USE_MQTT | ||||
							
								
								
									
										41
									
								
								esphome/components/mqtt/mqtt_valve.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								esphome/components/mqtt/mqtt_valve.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/defines.h" | ||||
| #include "mqtt_component.h" | ||||
|  | ||||
| #ifdef USE_MQTT | ||||
| #ifdef USE_VALVE | ||||
|  | ||||
| #include "esphome/components/valve/valve.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace mqtt { | ||||
|  | ||||
| class MQTTValveComponent : public mqtt::MQTTComponent { | ||||
|  public: | ||||
|   explicit MQTTValveComponent(valve::Valve *valve); | ||||
|  | ||||
|   void setup() override; | ||||
|   void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override; | ||||
|  | ||||
|   MQTT_COMPONENT_CUSTOM_TOPIC(position, command) | ||||
|   MQTT_COMPONENT_CUSTOM_TOPIC(position, state) | ||||
|  | ||||
|   bool send_initial_state() override; | ||||
|  | ||||
|   bool publish_state(); | ||||
|  | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   std::string component_type() const override; | ||||
|   const EntityBase *get_entity() const override; | ||||
|  | ||||
|   valve::Valve *valve_; | ||||
| }; | ||||
|  | ||||
| }  // namespace mqtt | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif | ||||
| #endif  // USE_MQTT | ||||
| @@ -881,6 +881,45 @@ async def pronto_action(var, config, args): | ||||
|     cg.add(var.set_data(template_)) | ||||
|  | ||||
|  | ||||
| # Roomba | ||||
| ( | ||||
|     RoombaData, | ||||
|     RoombaBinarySensor, | ||||
|     RoombaTrigger, | ||||
|     RoombaAction, | ||||
|     RoombaDumper, | ||||
| ) = declare_protocol("Roomba") | ||||
| ROOMBA_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint8_t}) | ||||
|  | ||||
|  | ||||
| @register_binary_sensor("roomba", RoombaBinarySensor, ROOMBA_SCHEMA) | ||||
| def roomba_binary_sensor(var, config): | ||||
|     cg.add( | ||||
|         var.set_data( | ||||
|             cg.StructInitializer( | ||||
|                 RoombaData, | ||||
|                 ("data", config[CONF_DATA]), | ||||
|             ) | ||||
|         ) | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @register_trigger("roomba", RoombaTrigger, RoombaData) | ||||
| def roomba_trigger(var, config): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| @register_dumper("roomba", RoombaDumper) | ||||
| def roomba_dumper(var, config): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| @register_action("roomba", RoombaAction, ROOMBA_SCHEMA) | ||||
| async def roomba_action(var, config, args): | ||||
|     template_ = await cg.templatable(config[CONF_DATA], args, cg.uint8) | ||||
|     cg.add(var.set_data(template_)) | ||||
|  | ||||
|  | ||||
| # Sony | ||||
| SonyData, SonyBinarySensor, SonyTrigger, SonyAction, SonyDumper = declare_protocol( | ||||
|     "Sony" | ||||
|   | ||||
							
								
								
									
										56
									
								
								esphome/components/remote_base/roomba_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								esphome/components/remote_base/roomba_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| #include "roomba_protocol.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace remote_base { | ||||
|  | ||||
| static const char *const TAG = "remote.roomba"; | ||||
|  | ||||
| static const uint8_t NBITS = 8; | ||||
| static const uint32_t BIT_ONE_HIGH_US = 3000; | ||||
| static const uint32_t BIT_ONE_LOW_US = 1000; | ||||
| static const uint32_t BIT_ZERO_HIGH_US = BIT_ONE_LOW_US; | ||||
| static const uint32_t BIT_ZERO_LOW_US = BIT_ONE_HIGH_US; | ||||
|  | ||||
| void RoombaProtocol::encode(RemoteTransmitData *dst, const RoombaData &data) { | ||||
|   dst->set_carrier_frequency(38000); | ||||
|   dst->reserve(NBITS * 2u); | ||||
|  | ||||
|   for (uint32_t mask = 1UL << (NBITS - 1); mask != 0; mask >>= 1) { | ||||
|     if (data.data & mask) { | ||||
|       dst->item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US); | ||||
|     } else { | ||||
|       dst->item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| optional<RoombaData> RoombaProtocol::decode(RemoteReceiveData src) { | ||||
|   RoombaData out{.data = 0}; | ||||
|  | ||||
|   for (uint8_t i = 0; i < (NBITS - 1); i++) { | ||||
|     out.data <<= 1UL; | ||||
|     if (src.expect_item(BIT_ONE_HIGH_US, BIT_ONE_LOW_US)) { | ||||
|       out.data |= 1UL; | ||||
|     } else if (src.expect_item(BIT_ZERO_HIGH_US, BIT_ZERO_LOW_US)) { | ||||
|       out.data |= 0UL; | ||||
|     } else { | ||||
|       return {}; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // not possible to measure space on last bit, check only mark | ||||
|   out.data <<= 1UL; | ||||
|   if (src.expect_mark(BIT_ONE_HIGH_US)) { | ||||
|     out.data |= 1UL; | ||||
|   } else if (src.expect_mark(BIT_ZERO_HIGH_US)) { | ||||
|     out.data |= 0UL; | ||||
|   } else { | ||||
|     return {}; | ||||
|   } | ||||
|  | ||||
|   return out; | ||||
| } | ||||
| void RoombaProtocol::dump(const RoombaData &data) { ESP_LOGD(TAG, "Received Roomba: data=0x%02X", data.data); } | ||||
|  | ||||
| }  // namespace remote_base | ||||
| }  // namespace esphome | ||||
							
								
								
									
										35
									
								
								esphome/components/remote_base/roomba_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								esphome/components/remote_base/roomba_protocol.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "remote_base.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace remote_base { | ||||
|  | ||||
| struct RoombaData { | ||||
|   uint8_t data; | ||||
|  | ||||
|   bool operator==(const RoombaData &rhs) const { return data == rhs.data; } | ||||
| }; | ||||
|  | ||||
| class RoombaProtocol : public RemoteProtocol<RoombaData> { | ||||
|  public: | ||||
|   void encode(RemoteTransmitData *dst, const RoombaData &data) override; | ||||
|   optional<RoombaData> decode(RemoteReceiveData src) override; | ||||
|   void dump(const RoombaData &data) override; | ||||
| }; | ||||
|  | ||||
| DECLARE_REMOTE_PROTOCOL(Roomba) | ||||
|  | ||||
| template<typename... Ts> class RoombaAction : public RemoteTransmitterActionBase<Ts...> { | ||||
|  public: | ||||
|   TEMPLATABLE_VALUE(uint8_t, data) | ||||
|  | ||||
|   void encode(RemoteTransmitData *dst, Ts... x) override { | ||||
|     RoombaData data{}; | ||||
|     data.data = this->data_.value(x...); | ||||
|     RoombaProtocol().encode(dst, data); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| }  // namespace remote_base | ||||
| }  // namespace esphome | ||||
| @@ -359,11 +359,15 @@ OrFilter::OrFilter(std::vector<Filter *> filters) : filters_(std::move(filters)) | ||||
| OrFilter::PhiNode::PhiNode(OrFilter *or_parent) : or_parent_(or_parent) {} | ||||
|  | ||||
| optional<float> OrFilter::PhiNode::new_value(float value) { | ||||
|   this->or_parent_->output(value); | ||||
|   if (!this->or_parent_->has_value_) { | ||||
|     this->or_parent_->output(value); | ||||
|     this->or_parent_->has_value_ = true; | ||||
|   } | ||||
|  | ||||
|   return {}; | ||||
| } | ||||
| optional<float> OrFilter::new_value(float value) { | ||||
|   this->has_value_ = false; | ||||
|   for (Filter *filter : this->filters_) | ||||
|     filter->input(value); | ||||
|  | ||||
|   | ||||
| @@ -389,6 +389,7 @@ class OrFilter : public Filter { | ||||
|   }; | ||||
|  | ||||
|   std::vector<Filter *> filters_; | ||||
|   bool has_value_{false}; | ||||
|   PhiNode phi_; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -6,9 +6,14 @@ namespace sht3xd { | ||||
|  | ||||
| static const char *const TAG = "sht3xd"; | ||||
|  | ||||
| // use read serial number register with clock stretching disabled as per other SHT3XD_COMMAND registers | ||||
| // which provides support for SHT85 sensor | ||||
| // SHT85 does not support clock stretching and uses same registers as SHT3xd with clock stretching disabled | ||||
| // https://sensirion.com/media/documents/E5762713/63D103C2/Sensirion_electronic_identification_code_SHT3x.pdf | ||||
| // indicates two possible read serial number registers either with clock stretching enabled or disabled. | ||||
| // Other SHT3XD_COMMAND registers use the clock stretching disabled register. | ||||
| // To ensure compatibility, reading serial number using the register with clock stretching register enabled | ||||
| // (used originally in this component) is tried first and if that fails the alternate register address | ||||
| // with clock stretching disabled is read. | ||||
|  | ||||
| static const uint16_t SHT3XD_COMMAND_READ_SERIAL_NUMBER_CLOCK_STRETCHING = 0x3780; | ||||
| static const uint16_t SHT3XD_COMMAND_READ_SERIAL_NUMBER = 0x3682; | ||||
|  | ||||
| static const uint16_t SHT3XD_COMMAND_READ_STATUS = 0xF32D; | ||||
| @@ -22,13 +27,19 @@ static const uint16_t SHT3XD_COMMAND_FETCH_DATA = 0xE000; | ||||
| void SHT3XDComponent::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up SHT3xD..."); | ||||
|   uint16_t raw_serial_number[2]; | ||||
|   if (!this->get_register(SHT3XD_COMMAND_READ_SERIAL_NUMBER, raw_serial_number, 2)) { | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   if (!this->get_register(SHT3XD_COMMAND_READ_SERIAL_NUMBER_CLOCK_STRETCHING, raw_serial_number, 2)) { | ||||
|     this->error_code_ = READ_SERIAL_STRETCHED_FAILED; | ||||
|     if (!this->get_register(SHT3XD_COMMAND_READ_SERIAL_NUMBER, raw_serial_number, 2)) { | ||||
|       this->error_code_ = READ_SERIAL_FAILED; | ||||
|       this->mark_failed(); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   this->serial_number_ = (uint32_t(raw_serial_number[0]) << 16) | uint32_t(raw_serial_number[1]); | ||||
|  | ||||
|   if (!this->write_command(heater_enabled_ ? SHT3XD_COMMAND_HEATER_ENABLE : SHT3XD_COMMAND_HEATER_DISABLE)) { | ||||
|     this->error_code_ = WRITE_HEATER_MODE_FAILED; | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
| @@ -36,10 +47,21 @@ void SHT3XDComponent::setup() { | ||||
|  | ||||
| void SHT3XDComponent::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "SHT3xD:"); | ||||
|   switch (this->error_code_) { | ||||
|     case READ_SERIAL_FAILED: | ||||
|       ESP_LOGD(TAG, "  Error reading serial number"); | ||||
|       break; | ||||
|     case WRITE_HEATER_MODE_FAILED: | ||||
|       ESP_LOGD(TAG, "  Error writing heater mode"); | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
|   if (this->is_failed()) { | ||||
|     ESP_LOGE(TAG, "  Communication with SHT3xD failed!"); | ||||
|     return; | ||||
|   } | ||||
|   ESP_LOGD(TAG, "  Setup successful"); | ||||
|   ESP_LOGD(TAG, "  Serial Number: 0x%08" PRIX32, this->serial_number_); | ||||
|   ESP_LOGD(TAG, "  Heater Enabled: %s", this->heater_enabled_ ? "true" : "false"); | ||||
|  | ||||
|   | ||||
| @@ -22,6 +22,13 @@ class SHT3XDComponent : public PollingComponent, public sensirion_common::Sensir | ||||
|   void set_heater_enabled(bool heater_enabled) { heater_enabled_ = heater_enabled; } | ||||
|  | ||||
|  protected: | ||||
|   enum ErrorCode { | ||||
|     NONE = 0, | ||||
|     READ_SERIAL_STRETCHED_FAILED, | ||||
|     READ_SERIAL_FAILED, | ||||
|     WRITE_HEATER_MODE_FAILED, | ||||
|   } error_code_{NONE}; | ||||
|  | ||||
|   sensor::Sensor *temperature_sensor_{nullptr}; | ||||
|   sensor::Sensor *humidity_sensor_{nullptr}; | ||||
|   bool heater_enabled_{true}; | ||||
|   | ||||
| @@ -4,14 +4,13 @@ from esphome import pins | ||||
| from esphome.components import spi | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_SPI_ID, | ||||
|     CONF_NUMBER, | ||||
|     CONF_INVERTED, | ||||
|     CONF_DATA_PIN, | ||||
|     CONF_CLOCK_PIN, | ||||
|     CONF_OUTPUT, | ||||
|     CONF_TYPE, | ||||
| ) | ||||
| from esphome.core import EsphomeError | ||||
|  | ||||
| MULTI_CONF = True | ||||
|  | ||||
| @@ -34,53 +33,53 @@ CONF_LATCH_PIN = "latch_pin" | ||||
| CONF_OE_PIN = "oe_pin" | ||||
| CONF_SR_COUNT = "sr_count" | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Any( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.declare_id(SN74HC595GPIOComponent), | ||||
|             cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, | ||||
|             cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, | ||||
|             cv.Required(CONF_LATCH_PIN): pins.gpio_output_pin_schema, | ||||
|             cv.Optional(CONF_OE_PIN): pins.gpio_output_pin_schema, | ||||
|             cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(min=1, max=256), | ||||
|         } | ||||
|     ).extend(cv.COMPONENT_SCHEMA), | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.declare_id(SN74HC595SPIComponent), | ||||
|             cv.Required(CONF_LATCH_PIN): pins.gpio_output_pin_schema, | ||||
|             cv.Optional(CONF_OE_PIN): pins.gpio_output_pin_schema, | ||||
|             cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(min=1, max=256), | ||||
|         } | ||||
|     ) | ||||
|     .extend(cv.COMPONENT_SCHEMA) | ||||
|     .extend(spi.spi_device_schema(cs_pin_required=False)) | ||||
|     .extend( | ||||
|         { | ||||
|             cv.Required(CONF_SPI_ID): cv.use_id(spi.SPIComponent), | ||||
|         } | ||||
|     ), | ||||
|     msg='Either "data_pin" and "clock_pin" must be set or "spi_id" must be set.', | ||||
| TYPE_GPIO = "gpio" | ||||
| TYPE_SPI = "spi" | ||||
|  | ||||
| _COMMON_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_LATCH_PIN): pins.gpio_output_pin_schema, | ||||
|         cv.Optional(CONF_OE_PIN): pins.gpio_output_pin_schema, | ||||
|         cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(min=1, max=256), | ||||
|     } | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.typed_schema( | ||||
|     { | ||||
|         TYPE_GPIO: _COMMON_SCHEMA.extend( | ||||
|             { | ||||
|                 cv.Required(CONF_ID): cv.declare_id(SN74HC595GPIOComponent), | ||||
|                 cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, | ||||
|                 cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, | ||||
|             } | ||||
|         ).extend(cv.COMPONENT_SCHEMA), | ||||
|         TYPE_SPI: _COMMON_SCHEMA.extend( | ||||
|             { | ||||
|                 cv.Required(CONF_ID): cv.declare_id(SN74HC595SPIComponent), | ||||
|             } | ||||
|         ) | ||||
|         .extend(cv.COMPONENT_SCHEMA) | ||||
|         .extend(spi.spi_device_schema(cs_pin_required=False)), | ||||
|     }, | ||||
|     default_type=TYPE_GPIO, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|     if CONF_DATA_PIN in config: | ||||
|     if config[CONF_TYPE] == TYPE_GPIO: | ||||
|         data_pin = await cg.gpio_pin_expression(config[CONF_DATA_PIN]) | ||||
|         cg.add(var.set_data_pin(data_pin)) | ||||
|         clock_pin = await cg.gpio_pin_expression(config[CONF_CLOCK_PIN]) | ||||
|         cg.add(var.set_clock_pin(clock_pin)) | ||||
|     elif CONF_SPI_ID in config: | ||||
|         await spi.register_spi_device(var, config) | ||||
|     else: | ||||
|         raise EsphomeError("Not supported") | ||||
|         await spi.register_spi_device(var, config) | ||||
|  | ||||
|     latch_pin = await cg.gpio_pin_expression(config[CONF_LATCH_PIN]) | ||||
|     cg.add(var.set_latch_pin(latch_pin)) | ||||
|     if CONF_OE_PIN in config: | ||||
|         oe_pin = await cg.gpio_pin_expression(config[CONF_OE_PIN]) | ||||
|     if oe_pin := config.get(CONF_OE_PIN): | ||||
|         oe_pin = await cg.gpio_pin_expression(oe_pin) | ||||
|         cg.add(var.set_oe_pin(oe_pin)) | ||||
|     cg.add(var.set_sr_count(config[CONF_SR_COUNT])) | ||||
|  | ||||
|   | ||||
							
								
								
									
										24
									
								
								esphome/components/template/event/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphome/components/template/event/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| import esphome.config_validation as cv | ||||
|  | ||||
| from esphome.components import event | ||||
|  | ||||
| import esphome.codegen as cg | ||||
|  | ||||
| from esphome.const import CONF_EVENT_TYPES | ||||
|  | ||||
| from .. import template_ns | ||||
|  | ||||
| CODEOWNERS = ["@nohat"] | ||||
|  | ||||
| TemplateEvent = template_ns.class_("TemplateEvent", event.Event, cg.Component) | ||||
|  | ||||
| CONFIG_SCHEMA = event.event_schema(TemplateEvent).extend( | ||||
|     { | ||||
|         cv.Required(CONF_EVENT_TYPES): cv.ensure_list(cv.string_strict), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = await event.new_event(config, event_types=config[CONF_EVENT_TYPES]) | ||||
|     await cg.register_component(var, config) | ||||
							
								
								
									
										12
									
								
								esphome/components/template/event/template_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/template/event/template_event.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/event/event.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace template_ { | ||||
|  | ||||
| class TemplateEvent : public Component, public event::Event {}; | ||||
|  | ||||
| }  // namespace template_ | ||||
| }  // namespace esphome | ||||
							
								
								
									
										118
									
								
								esphome/components/template/valve/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								esphome/components/template/valve/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| from esphome.components import valve | ||||
| from esphome.const import ( | ||||
|     CONF_ASSUMED_STATE, | ||||
|     CONF_CLOSE_ACTION, | ||||
|     CONF_CURRENT_OPERATION, | ||||
|     CONF_ID, | ||||
|     CONF_LAMBDA, | ||||
|     CONF_OPEN_ACTION, | ||||
|     CONF_OPTIMISTIC, | ||||
|     CONF_POSITION, | ||||
|     CONF_POSITION_ACTION, | ||||
|     CONF_RESTORE_MODE, | ||||
|     CONF_STATE, | ||||
|     CONF_STOP_ACTION, | ||||
| ) | ||||
| from .. import template_ns | ||||
|  | ||||
| TemplateValve = template_ns.class_("TemplateValve", valve.Valve, cg.Component) | ||||
|  | ||||
| TemplateValveRestoreMode = template_ns.enum("TemplateValveRestoreMode") | ||||
| RESTORE_MODES = { | ||||
|     "NO_RESTORE": TemplateValveRestoreMode.VALVE_NO_RESTORE, | ||||
|     "RESTORE": TemplateValveRestoreMode.VALVE_RESTORE, | ||||
|     "RESTORE_AND_CALL": TemplateValveRestoreMode.VALVE_RESTORE_AND_CALL, | ||||
| } | ||||
|  | ||||
| CONF_HAS_POSITION = "has_position" | ||||
| CONF_TOGGLE_ACTION = "toggle_action" | ||||
|  | ||||
| CONFIG_SCHEMA = valve.VALVE_SCHEMA.extend( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(TemplateValve), | ||||
|         cv.Optional(CONF_LAMBDA): cv.returning_lambda, | ||||
|         cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, | ||||
|         cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, | ||||
|         cv.Optional(CONF_HAS_POSITION, default=False): cv.boolean, | ||||
|         cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True), | ||||
|         cv.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True), | ||||
|         cv.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True), | ||||
|         cv.Optional(CONF_TOGGLE_ACTION): automation.validate_automation(single=True), | ||||
|         cv.Optional(CONF_POSITION_ACTION): automation.validate_automation(single=True), | ||||
|         cv.Optional(CONF_RESTORE_MODE, default="NO_RESTORE"): cv.enum( | ||||
|             RESTORE_MODES, upper=True | ||||
|         ), | ||||
|     } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = await valve.new_valve(config) | ||||
|     await cg.register_component(var, config) | ||||
|     if lambda_config := config.get(CONF_LAMBDA): | ||||
|         template_ = await cg.process_lambda( | ||||
|             lambda_config, [], return_type=cg.optional.template(float) | ||||
|         ) | ||||
|         cg.add(var.set_state_lambda(template_)) | ||||
|     if open_action_config := config.get(CONF_OPEN_ACTION): | ||||
|         await automation.build_automation( | ||||
|             var.get_open_trigger(), [], open_action_config | ||||
|         ) | ||||
|     if close_action_config := config.get(CONF_CLOSE_ACTION): | ||||
|         await automation.build_automation( | ||||
|             var.get_close_trigger(), [], close_action_config | ||||
|         ) | ||||
|     if stop_action_config := config.get(CONF_STOP_ACTION): | ||||
|         await automation.build_automation( | ||||
|             var.get_stop_trigger(), [], stop_action_config | ||||
|         ) | ||||
|         cg.add(var.set_has_stop(True)) | ||||
|     if toggle_action_config := config.get(CONF_TOGGLE_ACTION): | ||||
|         await automation.build_automation( | ||||
|             var.get_toggle_trigger(), [], toggle_action_config | ||||
|         ) | ||||
|         cg.add(var.set_has_toggle(True)) | ||||
|     if position_action_config := config.get(CONF_POSITION_ACTION): | ||||
|         await automation.build_automation( | ||||
|             var.get_position_trigger(), [(float, "pos")], position_action_config | ||||
|         ) | ||||
|         cg.add(var.set_has_position(True)) | ||||
|     else: | ||||
|         cg.add(var.set_has_position(config[CONF_HAS_POSITION])) | ||||
|     cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) | ||||
|     cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE])) | ||||
|     cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) | ||||
|  | ||||
|  | ||||
| @automation.register_action( | ||||
|     "valve.template.publish", | ||||
|     valve.ValvePublishAction, | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.use_id(valve.Valve), | ||||
|             cv.Exclusive(CONF_STATE, "pos"): cv.templatable(valve.validate_valve_state), | ||||
|             cv.Exclusive(CONF_POSITION, "pos"): cv.templatable(cv.percentage), | ||||
|             cv.Optional(CONF_CURRENT_OPERATION): cv.templatable( | ||||
|                 valve.validate_valve_operation | ||||
|             ), | ||||
|         } | ||||
|     ), | ||||
| ) | ||||
| async def valve_template_publish_to_code(config, action_id, template_arg, args): | ||||
|     paren = await cg.get_variable(config[CONF_ID]) | ||||
|     var = cg.new_Pvariable(action_id, template_arg, paren) | ||||
|     if state_config := config.get(CONF_STATE): | ||||
|         template_ = await cg.templatable(state_config, args, float) | ||||
|         cg.add(var.set_position(template_)) | ||||
|     if (position_config := config.get(CONF_POSITION)) is not None: | ||||
|         template_ = await cg.templatable(position_config, args, float) | ||||
|         cg.add(var.set_position(template_)) | ||||
|     if current_operation_config := config.get(CONF_CURRENT_OPERATION): | ||||
|         template_ = await cg.templatable( | ||||
|             current_operation_config, args, valve.ValveOperation | ||||
|         ) | ||||
|         cg.add(var.set_current_operation(template_)) | ||||
|     return var | ||||
							
								
								
									
										131
									
								
								esphome/components/template/valve/template_valve.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								esphome/components/template/valve/template_valve.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | ||||
| #include "template_valve.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace template_ { | ||||
|  | ||||
| using namespace esphome::valve; | ||||
|  | ||||
| static const char *const TAG = "template.valve"; | ||||
|  | ||||
| TemplateValve::TemplateValve() | ||||
|     : open_trigger_(new Trigger<>()), | ||||
|       close_trigger_(new Trigger<>), | ||||
|       stop_trigger_(new Trigger<>()), | ||||
|       toggle_trigger_(new Trigger<>()), | ||||
|       position_trigger_(new Trigger<float>()) {} | ||||
|  | ||||
| void TemplateValve::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up template valve '%s'...", this->name_.c_str()); | ||||
|   switch (this->restore_mode_) { | ||||
|     case VALVE_NO_RESTORE: | ||||
|       break; | ||||
|     case VALVE_RESTORE: { | ||||
|       auto restore = this->restore_state_(); | ||||
|       if (restore.has_value()) | ||||
|         restore->apply(this); | ||||
|       break; | ||||
|     } | ||||
|     case VALVE_RESTORE_AND_CALL: { | ||||
|       auto restore = this->restore_state_(); | ||||
|       if (restore.has_value()) { | ||||
|         restore->to_call(this).perform(); | ||||
|       } | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void TemplateValve::loop() { | ||||
|   bool changed = false; | ||||
|  | ||||
|   if (this->state_f_.has_value()) { | ||||
|     auto s = (*this->state_f_)(); | ||||
|     if (s.has_value()) { | ||||
|       auto pos = clamp(*s, 0.0f, 1.0f); | ||||
|       if (pos != this->position) { | ||||
|         this->position = pos; | ||||
|         changed = true; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (changed) | ||||
|     this->publish_state(); | ||||
| } | ||||
|  | ||||
| void TemplateValve::set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } | ||||
| void TemplateValve::set_assumed_state(bool assumed_state) { this->assumed_state_ = assumed_state; } | ||||
| void TemplateValve::set_state_lambda(std::function<optional<float>()> &&f) { this->state_f_ = f; } | ||||
| float TemplateValve::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||
|  | ||||
| Trigger<> *TemplateValve::get_open_trigger() const { return this->open_trigger_; } | ||||
| Trigger<> *TemplateValve::get_close_trigger() const { return this->close_trigger_; } | ||||
| Trigger<> *TemplateValve::get_stop_trigger() const { return this->stop_trigger_; } | ||||
| Trigger<> *TemplateValve::get_toggle_trigger() const { return this->toggle_trigger_; } | ||||
|  | ||||
| void TemplateValve::dump_config() { | ||||
|   LOG_VALVE("", "Template Valve", this); | ||||
|   ESP_LOGCONFIG(TAG, "  Has position: %s", YESNO(this->has_position_)); | ||||
|   ESP_LOGCONFIG(TAG, "  Optimistic: %s", YESNO(this->optimistic_)); | ||||
| } | ||||
|  | ||||
| void TemplateValve::control(const ValveCall &call) { | ||||
|   if (call.get_stop()) { | ||||
|     this->stop_prev_trigger_(); | ||||
|     this->stop_trigger_->trigger(); | ||||
|     this->prev_command_trigger_ = this->stop_trigger_; | ||||
|     this->publish_state(); | ||||
|   } | ||||
|   if (call.get_toggle().has_value()) { | ||||
|     this->stop_prev_trigger_(); | ||||
|     this->toggle_trigger_->trigger(); | ||||
|     this->prev_command_trigger_ = this->toggle_trigger_; | ||||
|     this->publish_state(); | ||||
|   } | ||||
|   if (call.get_position().has_value()) { | ||||
|     auto pos = *call.get_position(); | ||||
|     this->stop_prev_trigger_(); | ||||
|  | ||||
|     if (pos == VALVE_OPEN) { | ||||
|       this->open_trigger_->trigger(); | ||||
|       this->prev_command_trigger_ = this->open_trigger_; | ||||
|     } else if (pos == VALVE_CLOSED) { | ||||
|       this->close_trigger_->trigger(); | ||||
|       this->prev_command_trigger_ = this->close_trigger_; | ||||
|     } else { | ||||
|       this->position_trigger_->trigger(pos); | ||||
|     } | ||||
|  | ||||
|     if (this->optimistic_) { | ||||
|       this->position = pos; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   this->publish_state(); | ||||
| } | ||||
|  | ||||
| ValveTraits TemplateValve::get_traits() { | ||||
|   auto traits = ValveTraits(); | ||||
|   traits.set_is_assumed_state(this->assumed_state_); | ||||
|   traits.set_supports_stop(this->has_stop_); | ||||
|   traits.set_supports_toggle(this->has_toggle_); | ||||
|   traits.set_supports_position(this->has_position_); | ||||
|   return traits; | ||||
| } | ||||
|  | ||||
| Trigger<float> *TemplateValve::get_position_trigger() const { return this->position_trigger_; } | ||||
|  | ||||
| void TemplateValve::set_has_stop(bool has_stop) { this->has_stop_ = has_stop; } | ||||
| void TemplateValve::set_has_toggle(bool has_toggle) { this->has_toggle_ = has_toggle; } | ||||
| void TemplateValve::set_has_position(bool has_position) { this->has_position_ = has_position; } | ||||
|  | ||||
| void TemplateValve::stop_prev_trigger_() { | ||||
|   if (this->prev_command_trigger_ != nullptr) { | ||||
|     this->prev_command_trigger_->stop_action(); | ||||
|     this->prev_command_trigger_ = nullptr; | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace template_ | ||||
| }  // namespace esphome | ||||
							
								
								
									
										60
									
								
								esphome/components/template/valve/template_valve.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								esphome/components/template/valve/template_valve.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/components/valve/valve.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace template_ { | ||||
|  | ||||
| enum TemplateValveRestoreMode { | ||||
|   VALVE_NO_RESTORE, | ||||
|   VALVE_RESTORE, | ||||
|   VALVE_RESTORE_AND_CALL, | ||||
| }; | ||||
|  | ||||
| class TemplateValve : public valve::Valve, public Component { | ||||
|  public: | ||||
|   TemplateValve(); | ||||
|  | ||||
|   void set_state_lambda(std::function<optional<float>()> &&f); | ||||
|   Trigger<> *get_open_trigger() const; | ||||
|   Trigger<> *get_close_trigger() const; | ||||
|   Trigger<> *get_stop_trigger() const; | ||||
|   Trigger<> *get_toggle_trigger() const; | ||||
|   Trigger<float> *get_position_trigger() const; | ||||
|   void set_optimistic(bool optimistic); | ||||
|   void set_assumed_state(bool assumed_state); | ||||
|   void set_has_stop(bool has_stop); | ||||
|   void set_has_position(bool has_position); | ||||
|   void set_has_toggle(bool has_toggle); | ||||
|   void set_restore_mode(TemplateValveRestoreMode restore_mode) { restore_mode_ = restore_mode; } | ||||
|  | ||||
|   void setup() override; | ||||
|   void loop() override; | ||||
|   void dump_config() override; | ||||
|  | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
|  protected: | ||||
|   void control(const valve::ValveCall &call) override; | ||||
|   valve::ValveTraits get_traits() override; | ||||
|   void stop_prev_trigger_(); | ||||
|  | ||||
|   TemplateValveRestoreMode restore_mode_{VALVE_NO_RESTORE}; | ||||
|   optional<std::function<optional<float>()>> state_f_; | ||||
|   bool assumed_state_{false}; | ||||
|   bool optimistic_{false}; | ||||
|   Trigger<> *open_trigger_; | ||||
|   Trigger<> *close_trigger_; | ||||
|   bool has_stop_{false}; | ||||
|   bool has_toggle_{false}; | ||||
|   Trigger<> *stop_trigger_; | ||||
|   Trigger<> *toggle_trigger_; | ||||
|   Trigger<> *prev_command_trigger_{nullptr}; | ||||
|   Trigger<float> *position_trigger_; | ||||
|   bool has_position_{false}; | ||||
| }; | ||||
|  | ||||
| }  // namespace template_ | ||||
| }  // namespace esphome | ||||
| @@ -59,6 +59,7 @@ UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action) | ||||
| UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action) | ||||
| UARTDummyReceiver = uart_ns.class_("UARTDummyReceiver", cg.Component) | ||||
| MULTI_CONF = True | ||||
| MULTI_CONF_NO_DEFAULT = True | ||||
|  | ||||
|  | ||||
| def validate_raw_data(value): | ||||
|   | ||||
							
								
								
									
										186
									
								
								esphome/components/valve/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								esphome/components/valve/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| from esphome.automation import maybe_simple_id, Condition | ||||
| from esphome.components import mqtt | ||||
| from esphome.const import ( | ||||
|     CONF_DEVICE_CLASS, | ||||
|     CONF_ID, | ||||
|     CONF_MQTT_ID, | ||||
|     CONF_ON_OPEN, | ||||
|     CONF_POSITION, | ||||
|     CONF_POSITION_COMMAND_TOPIC, | ||||
|     CONF_POSITION_STATE_TOPIC, | ||||
|     CONF_STATE, | ||||
|     CONF_STOP, | ||||
|     CONF_TRIGGER_ID, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.cpp_helpers import setup_entity | ||||
|  | ||||
| IS_PLATFORM_COMPONENT = True | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
|  | ||||
| valve_ns = cg.esphome_ns.namespace("valve") | ||||
|  | ||||
| Valve = valve_ns.class_("Valve", cg.EntityBase) | ||||
|  | ||||
| VALVE_OPEN = valve_ns.VALVE_OPEN | ||||
| VALVE_CLOSED = valve_ns.VALVE_CLOSED | ||||
|  | ||||
| VALVE_STATES = { | ||||
|     "OPEN": VALVE_OPEN, | ||||
|     "CLOSED": VALVE_CLOSED, | ||||
| } | ||||
| validate_valve_state = cv.enum(VALVE_STATES, upper=True) | ||||
|  | ||||
| ValveOperation = valve_ns.enum("ValveOperation") | ||||
| VALVE_OPERATIONS = { | ||||
|     "IDLE": ValveOperation.VALVE_OPERATION_IDLE, | ||||
|     "OPENING": ValveOperation.VALVE_OPERATION_OPENING, | ||||
|     "CLOSING": ValveOperation.VALVE_OPERATION_CLOSING, | ||||
| } | ||||
| validate_valve_operation = cv.enum(VALVE_OPERATIONS, upper=True) | ||||
|  | ||||
| # Actions | ||||
| OpenAction = valve_ns.class_("OpenAction", automation.Action) | ||||
| CloseAction = valve_ns.class_("CloseAction", automation.Action) | ||||
| StopAction = valve_ns.class_("StopAction", automation.Action) | ||||
| ToggleAction = valve_ns.class_("ToggleAction", automation.Action) | ||||
| ControlAction = valve_ns.class_("ControlAction", automation.Action) | ||||
| ValvePublishAction = valve_ns.class_("ValvePublishAction", automation.Action) | ||||
| ValveIsOpenCondition = valve_ns.class_("ValveIsOpenCondition", Condition) | ||||
| ValveIsClosedCondition = valve_ns.class_("ValveIsClosedCondition", Condition) | ||||
|  | ||||
| # Triggers | ||||
| ValveOpenTrigger = valve_ns.class_("ValveOpenTrigger", automation.Trigger.template()) | ||||
| ValveClosedTrigger = valve_ns.class_( | ||||
|     "ValveClosedTrigger", automation.Trigger.template() | ||||
| ) | ||||
|  | ||||
| CONF_ON_CLOSED = "on_closed" | ||||
|  | ||||
| VALVE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(Valve), | ||||
|         cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTValveComponent), | ||||
|         cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All( | ||||
|             cv.requires_component("mqtt"), cv.subscribe_topic | ||||
|         ), | ||||
|         cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All( | ||||
|             cv.requires_component("mqtt"), cv.subscribe_topic | ||||
|         ), | ||||
|         cv.Optional(CONF_ON_OPEN): automation.validate_automation( | ||||
|             { | ||||
|                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValveOpenTrigger), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_ON_CLOSED): automation.validate_automation( | ||||
|             { | ||||
|                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValveClosedTrigger), | ||||
|             } | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def setup_valve_core_(var, config): | ||||
|     await setup_entity(var, config) | ||||
|  | ||||
|     if device_class_config := config.get(CONF_DEVICE_CLASS): | ||||
|         cg.add(var.set_device_class(device_class_config)) | ||||
|  | ||||
|     for conf in config.get(CONF_ON_OPEN, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation(trigger, [], conf) | ||||
|     for conf in config.get(CONF_ON_CLOSED, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation(trigger, [], conf) | ||||
|  | ||||
|     if mqtt_id_config := config.get(CONF_MQTT_ID): | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id_config, var) | ||||
|         await mqtt.register_mqtt_component(mqtt_, config) | ||||
|  | ||||
|         if position_state_topic_config := config.get(CONF_POSITION_STATE_TOPIC): | ||||
|             cg.add(mqtt_.set_custom_position_state_topic(position_state_topic_config)) | ||||
|         if position_command_topic_config := config.get(CONF_POSITION_COMMAND_TOPIC): | ||||
|             cg.add( | ||||
|                 mqtt_.set_custom_position_command_topic(position_command_topic_config) | ||||
|             ) | ||||
|  | ||||
|  | ||||
| async def register_valve(var, config): | ||||
|     if not CORE.has_id(config[CONF_ID]): | ||||
|         var = cg.Pvariable(config[CONF_ID], var) | ||||
|     cg.add(cg.App.register_valve(var)) | ||||
|     await setup_valve_core_(var, config) | ||||
|  | ||||
|  | ||||
| async def new_valve(config, *args): | ||||
|     var = cg.new_Pvariable(config[CONF_ID], *args) | ||||
|     await register_valve(var, config) | ||||
|     return var | ||||
|  | ||||
|  | ||||
| VALVE_ACTION_SCHEMA = maybe_simple_id( | ||||
|     { | ||||
|         cv.Required(CONF_ID): cv.use_id(Valve), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| @automation.register_action("valve.open", OpenAction, VALVE_ACTION_SCHEMA) | ||||
| async def valve_open_to_code(config, action_id, template_arg, args): | ||||
|     paren = await cg.get_variable(config[CONF_ID]) | ||||
|     return cg.new_Pvariable(action_id, template_arg, paren) | ||||
|  | ||||
|  | ||||
| @automation.register_action("valve.close", CloseAction, VALVE_ACTION_SCHEMA) | ||||
| async def valve_close_to_code(config, action_id, template_arg, args): | ||||
|     paren = await cg.get_variable(config[CONF_ID]) | ||||
|     return cg.new_Pvariable(action_id, template_arg, paren) | ||||
|  | ||||
|  | ||||
| @automation.register_action("valve.stop", StopAction, VALVE_ACTION_SCHEMA) | ||||
| async def valve_stop_to_code(config, action_id, template_arg, args): | ||||
|     paren = await cg.get_variable(config[CONF_ID]) | ||||
|     return cg.new_Pvariable(action_id, template_arg, paren) | ||||
|  | ||||
|  | ||||
| @automation.register_action("valve.toggle", ToggleAction, VALVE_ACTION_SCHEMA) | ||||
| def valve_toggle_to_code(config, action_id, template_arg, args): | ||||
|     paren = yield cg.get_variable(config[CONF_ID]) | ||||
|     yield cg.new_Pvariable(action_id, template_arg, paren) | ||||
|  | ||||
|  | ||||
| VALVE_CONTROL_ACTION_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_ID): cv.use_id(Valve), | ||||
|         cv.Optional(CONF_STOP): cv.templatable(cv.boolean), | ||||
|         cv.Exclusive(CONF_STATE, "pos"): cv.templatable(validate_valve_state), | ||||
|         cv.Exclusive(CONF_POSITION, "pos"): cv.templatable(cv.percentage), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| @automation.register_action("valve.control", ControlAction, VALVE_CONTROL_ACTION_SCHEMA) | ||||
| async def valve_control_to_code(config, action_id, template_arg, args): | ||||
|     paren = await cg.get_variable(config[CONF_ID]) | ||||
|     var = cg.new_Pvariable(action_id, template_arg, paren) | ||||
|     if stop_config := config.get(CONF_STOP): | ||||
|         template_ = await cg.templatable(stop_config, args, bool) | ||||
|         cg.add(var.set_stop(template_)) | ||||
|     if state_config := config.get(CONF_STATE): | ||||
|         template_ = await cg.templatable(state_config, args, float) | ||||
|         cg.add(var.set_position(template_)) | ||||
|     if (position_config := config.get(CONF_POSITION)) is not None: | ||||
|         template_ = await cg.templatable(position_config, args, float) | ||||
|         cg.add(var.set_position(template_)) | ||||
|     return var | ||||
|  | ||||
|  | ||||
| @coroutine_with_priority(100.0) | ||||
| async def to_code(config): | ||||
|     cg.add_define("USE_VALVE") | ||||
|     cg.add_global(valve_ns.using) | ||||
							
								
								
									
										129
									
								
								esphome/components/valve/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								esphome/components/valve/automation.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "valve.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace valve { | ||||
|  | ||||
| template<typename... Ts> class OpenAction : public Action<Ts...> { | ||||
|  public: | ||||
|   explicit OpenAction(Valve *valve) : valve_(valve) {} | ||||
|  | ||||
|   void play(Ts... x) override { this->valve_->make_call().set_command_open().perform(); } | ||||
|  | ||||
|  protected: | ||||
|   Valve *valve_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class CloseAction : public Action<Ts...> { | ||||
|  public: | ||||
|   explicit CloseAction(Valve *valve) : valve_(valve) {} | ||||
|  | ||||
|   void play(Ts... x) override { this->valve_->make_call().set_command_close().perform(); } | ||||
|  | ||||
|  protected: | ||||
|   Valve *valve_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class StopAction : public Action<Ts...> { | ||||
|  public: | ||||
|   explicit StopAction(Valve *valve) : valve_(valve) {} | ||||
|  | ||||
|   void play(Ts... x) override { this->valve_->make_call().set_command_stop().perform(); } | ||||
|  | ||||
|  protected: | ||||
|   Valve *valve_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class ToggleAction : public Action<Ts...> { | ||||
|  public: | ||||
|   explicit ToggleAction(Valve *valve) : valve_(valve) {} | ||||
|  | ||||
|   void play(Ts... x) override { this->valve_->make_call().set_command_toggle().perform(); } | ||||
|  | ||||
|  protected: | ||||
|   Valve *valve_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class ControlAction : public Action<Ts...> { | ||||
|  public: | ||||
|   explicit ControlAction(Valve *valve) : valve_(valve) {} | ||||
|  | ||||
|   TEMPLATABLE_VALUE(bool, stop) | ||||
|   TEMPLATABLE_VALUE(float, position) | ||||
|  | ||||
|   void play(Ts... x) override { | ||||
|     auto call = this->valve_->make_call(); | ||||
|     if (this->stop_.has_value()) | ||||
|       call.set_stop(this->stop_.value(x...)); | ||||
|     if (this->position_.has_value()) | ||||
|       call.set_position(this->position_.value(x...)); | ||||
|     call.perform(); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   Valve *valve_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class ValvePublishAction : public Action<Ts...> { | ||||
|  public: | ||||
|   ValvePublishAction(Valve *valve) : valve_(valve) {} | ||||
|   TEMPLATABLE_VALUE(float, position) | ||||
|   TEMPLATABLE_VALUE(ValveOperation, current_operation) | ||||
|  | ||||
|   void play(Ts... x) override { | ||||
|     if (this->position_.has_value()) | ||||
|       this->valve_->position = this->position_.value(x...); | ||||
|     if (this->current_operation_.has_value()) | ||||
|       this->valve_->current_operation = this->current_operation_.value(x...); | ||||
|     this->valve_->publish_state(); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   Valve *valve_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class ValveIsOpenCondition : public Condition<Ts...> { | ||||
|  public: | ||||
|   ValveIsOpenCondition(Valve *valve) : valve_(valve) {} | ||||
|   bool check(Ts... x) override { return this->valve_->is_fully_open(); } | ||||
|  | ||||
|  protected: | ||||
|   Valve *valve_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class ValveIsClosedCondition : public Condition<Ts...> { | ||||
|  public: | ||||
|   ValveIsClosedCondition(Valve *valve) : valve_(valve) {} | ||||
|   bool check(Ts... x) override { return this->valve_->is_fully_closed(); } | ||||
|  | ||||
|  protected: | ||||
|   Valve *valve_; | ||||
| }; | ||||
|  | ||||
| class ValveOpenTrigger : public Trigger<> { | ||||
|  public: | ||||
|   ValveOpenTrigger(Valve *a_valve) { | ||||
|     a_valve->add_on_state_callback([this, a_valve]() { | ||||
|       if (a_valve->is_fully_open()) { | ||||
|         this->trigger(); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| class ValveClosedTrigger : public Trigger<> { | ||||
|  public: | ||||
|   ValveClosedTrigger(Valve *a_valve) { | ||||
|     a_valve->add_on_state_callback([this, a_valve]() { | ||||
|       if (a_valve->is_fully_closed()) { | ||||
|         this->trigger(); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| }  // namespace valve | ||||
| }  // namespace esphome | ||||
							
								
								
									
										179
									
								
								esphome/components/valve/valve.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								esphome/components/valve/valve.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| #include "valve.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace valve { | ||||
|  | ||||
| static const char *const TAG = "valve"; | ||||
|  | ||||
| const float VALVE_OPEN = 1.0f; | ||||
| const float VALVE_CLOSED = 0.0f; | ||||
|  | ||||
| const char *valve_command_to_str(float pos) { | ||||
|   if (pos == VALVE_OPEN) { | ||||
|     return "OPEN"; | ||||
|   } else if (pos == VALVE_CLOSED) { | ||||
|     return "CLOSE"; | ||||
|   } else { | ||||
|     return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
| const char *valve_operation_to_str(ValveOperation op) { | ||||
|   switch (op) { | ||||
|     case VALVE_OPERATION_IDLE: | ||||
|       return "IDLE"; | ||||
|     case VALVE_OPERATION_OPENING: | ||||
|       return "OPENING"; | ||||
|     case VALVE_OPERATION_CLOSING: | ||||
|       return "CLOSING"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| Valve::Valve() : position{VALVE_OPEN} {} | ||||
|  | ||||
| ValveCall::ValveCall(Valve *parent) : parent_(parent) {} | ||||
| ValveCall &ValveCall::set_command(const char *command) { | ||||
|   if (strcasecmp(command, "OPEN") == 0) { | ||||
|     this->set_command_open(); | ||||
|   } else if (strcasecmp(command, "CLOSE") == 0) { | ||||
|     this->set_command_close(); | ||||
|   } else if (strcasecmp(command, "STOP") == 0) { | ||||
|     this->set_command_stop(); | ||||
|   } else if (strcasecmp(command, "TOGGLE") == 0) { | ||||
|     this->set_command_toggle(); | ||||
|   } else { | ||||
|     ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command); | ||||
|   } | ||||
|   return *this; | ||||
| } | ||||
| ValveCall &ValveCall::set_command_open() { | ||||
|   this->position_ = VALVE_OPEN; | ||||
|   return *this; | ||||
| } | ||||
| ValveCall &ValveCall::set_command_close() { | ||||
|   this->position_ = VALVE_CLOSED; | ||||
|   return *this; | ||||
| } | ||||
| ValveCall &ValveCall::set_command_stop() { | ||||
|   this->stop_ = true; | ||||
|   return *this; | ||||
| } | ||||
| ValveCall &ValveCall::set_command_toggle() { | ||||
|   this->toggle_ = true; | ||||
|   return *this; | ||||
| } | ||||
| ValveCall &ValveCall::set_position(float position) { | ||||
|   this->position_ = position; | ||||
|   return *this; | ||||
| } | ||||
| void ValveCall::perform() { | ||||
|   ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); | ||||
|   auto traits = this->parent_->get_traits(); | ||||
|   this->validate_(); | ||||
|   if (this->stop_) { | ||||
|     ESP_LOGD(TAG, "  Command: STOP"); | ||||
|   } | ||||
|   if (this->position_.has_value()) { | ||||
|     if (traits.get_supports_position()) { | ||||
|       ESP_LOGD(TAG, "  Position: %.0f%%", *this->position_ * 100.0f); | ||||
|     } else { | ||||
|       ESP_LOGD(TAG, "  Command: %s", valve_command_to_str(*this->position_)); | ||||
|     } | ||||
|   } | ||||
|   if (this->toggle_.has_value()) { | ||||
|     ESP_LOGD(TAG, "  Command: TOGGLE"); | ||||
|   } | ||||
|   this->parent_->control(*this); | ||||
| } | ||||
| const optional<float> &ValveCall::get_position() const { return this->position_; } | ||||
| const optional<bool> &ValveCall::get_toggle() const { return this->toggle_; } | ||||
| void ValveCall::validate_() { | ||||
|   auto traits = this->parent_->get_traits(); | ||||
|   if (this->position_.has_value()) { | ||||
|     auto pos = *this->position_; | ||||
|     if (!traits.get_supports_position() && pos != VALVE_OPEN && pos != VALVE_CLOSED) { | ||||
|       ESP_LOGW(TAG, "'%s' - This valve device does not support setting position!", this->parent_->get_name().c_str()); | ||||
|       this->position_.reset(); | ||||
|     } else if (pos < 0.0f || pos > 1.0f) { | ||||
|       ESP_LOGW(TAG, "'%s' - Position %.2f is out of range [0.0 - 1.0]", this->parent_->get_name().c_str(), pos); | ||||
|       this->position_ = clamp(pos, 0.0f, 1.0f); | ||||
|     } | ||||
|   } | ||||
|   if (this->toggle_.has_value()) { | ||||
|     if (!traits.get_supports_toggle()) { | ||||
|       ESP_LOGW(TAG, "'%s' - This valve device does not support toggle!", this->parent_->get_name().c_str()); | ||||
|       this->toggle_.reset(); | ||||
|     } | ||||
|   } | ||||
|   if (this->stop_) { | ||||
|     if (this->position_.has_value()) { | ||||
|       ESP_LOGW(TAG, "Cannot set position when stopping a valve!"); | ||||
|       this->position_.reset(); | ||||
|     } | ||||
|     if (this->toggle_.has_value()) { | ||||
|       ESP_LOGW(TAG, "Cannot set toggle when stopping a valve!"); | ||||
|       this->toggle_.reset(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| ValveCall &ValveCall::set_stop(bool stop) { | ||||
|   this->stop_ = stop; | ||||
|   return *this; | ||||
| } | ||||
| bool ValveCall::get_stop() const { return this->stop_; } | ||||
|  | ||||
| ValveCall Valve::make_call() { return {this}; } | ||||
|  | ||||
| void Valve::add_on_state_callback(std::function<void()> &&f) { this->state_callback_.add(std::move(f)); } | ||||
| void Valve::publish_state(bool save) { | ||||
|   this->position = clamp(this->position, 0.0f, 1.0f); | ||||
|  | ||||
|   ESP_LOGD(TAG, "'%s' - Publishing:", this->name_.c_str()); | ||||
|   auto traits = this->get_traits(); | ||||
|   if (traits.get_supports_position()) { | ||||
|     ESP_LOGD(TAG, "  Position: %.0f%%", this->position * 100.0f); | ||||
|   } else { | ||||
|     if (this->position == VALVE_OPEN) { | ||||
|       ESP_LOGD(TAG, "  State: OPEN"); | ||||
|     } else if (this->position == VALVE_CLOSED) { | ||||
|       ESP_LOGD(TAG, "  State: CLOSED"); | ||||
|     } else { | ||||
|       ESP_LOGD(TAG, "  State: UNKNOWN"); | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGD(TAG, "  Current Operation: %s", valve_operation_to_str(this->current_operation)); | ||||
|  | ||||
|   this->state_callback_.call(); | ||||
|  | ||||
|   if (save) { | ||||
|     ValveRestoreState restore{}; | ||||
|     memset(&restore, 0, sizeof(restore)); | ||||
|     restore.position = this->position; | ||||
|     this->rtc_.save(&restore); | ||||
|   } | ||||
| } | ||||
| optional<ValveRestoreState> Valve::restore_state_() { | ||||
|   this->rtc_ = global_preferences->make_preference<ValveRestoreState>(this->get_object_id_hash()); | ||||
|   ValveRestoreState recovered{}; | ||||
|   if (!this->rtc_.load(&recovered)) | ||||
|     return {}; | ||||
|   return recovered; | ||||
| } | ||||
|  | ||||
| bool Valve::is_fully_open() const { return this->position == VALVE_OPEN; } | ||||
| bool Valve::is_fully_closed() const { return this->position == VALVE_CLOSED; } | ||||
|  | ||||
| ValveCall ValveRestoreState::to_call(Valve *valve) { | ||||
|   auto call = valve->make_call(); | ||||
|   call.set_position(this->position); | ||||
|   return call; | ||||
| } | ||||
| void ValveRestoreState::apply(Valve *valve) { | ||||
|   valve->position = this->position; | ||||
|   valve->publish_state(); | ||||
| } | ||||
|  | ||||
| }  // namespace valve | ||||
| }  // namespace esphome | ||||
							
								
								
									
										152
									
								
								esphome/components/valve/valve.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								esphome/components/valve/valve.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/entity_base.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/preferences.h" | ||||
| #include "valve_traits.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace valve { | ||||
|  | ||||
| const extern float VALVE_OPEN; | ||||
| const extern float VALVE_CLOSED; | ||||
|  | ||||
| #define LOG_VALVE(prefix, type, obj) \ | ||||
|   if ((obj) != nullptr) { \ | ||||
|     ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ | ||||
|     auto traits_ = (obj)->get_traits(); \ | ||||
|     if (traits_.get_is_assumed_state()) { \ | ||||
|       ESP_LOGCONFIG(TAG, "%s  Assumed State: YES", prefix); \ | ||||
|     } \ | ||||
|     if (!(obj)->get_device_class().empty()) { \ | ||||
|       ESP_LOGCONFIG(TAG, "%s  Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ | ||||
|     } \ | ||||
|   } | ||||
|  | ||||
| class Valve; | ||||
|  | ||||
| class ValveCall { | ||||
|  public: | ||||
|   ValveCall(Valve *parent); | ||||
|  | ||||
|   /// Set the command as a string, "STOP", "OPEN", "CLOSE", "TOGGLE". | ||||
|   ValveCall &set_command(const char *command); | ||||
|   /// Set the command to open the valve. | ||||
|   ValveCall &set_command_open(); | ||||
|   /// Set the command to close the valve. | ||||
|   ValveCall &set_command_close(); | ||||
|   /// Set the command to stop the valve. | ||||
|   ValveCall &set_command_stop(); | ||||
|   /// Set the command to toggle the valve. | ||||
|   ValveCall &set_command_toggle(); | ||||
|   /// Set the call to a certain target position. | ||||
|   ValveCall &set_position(float position); | ||||
|   /// Set whether this valve call should stop the valve. | ||||
|   ValveCall &set_stop(bool stop); | ||||
|  | ||||
|   /// Perform the valve call. | ||||
|   void perform(); | ||||
|  | ||||
|   const optional<float> &get_position() const; | ||||
|   bool get_stop() const; | ||||
|   const optional<bool> &get_toggle() const; | ||||
|  | ||||
|  protected: | ||||
|   void validate_(); | ||||
|  | ||||
|   Valve *parent_; | ||||
|   bool stop_{false}; | ||||
|   optional<float> position_{}; | ||||
|   optional<bool> toggle_{}; | ||||
| }; | ||||
|  | ||||
| /// Struct used to store the restored state of a valve | ||||
| struct ValveRestoreState { | ||||
|   float position; | ||||
|  | ||||
|   /// Convert this struct to a valve call that can be performed. | ||||
|   ValveCall to_call(Valve *valve); | ||||
|   /// Apply these settings to the valve | ||||
|   void apply(Valve *valve); | ||||
| } __attribute__((packed)); | ||||
|  | ||||
| /// Enum encoding the current operation of a valve. | ||||
| enum ValveOperation : uint8_t { | ||||
|   /// The valve is currently idle (not moving) | ||||
|   VALVE_OPERATION_IDLE = 0, | ||||
|   /// The valve is currently opening. | ||||
|   VALVE_OPERATION_OPENING, | ||||
|   /// The valve is currently closing. | ||||
|   VALVE_OPERATION_CLOSING, | ||||
| }; | ||||
|  | ||||
| const char *valve_operation_to_str(ValveOperation op); | ||||
|  | ||||
| /** Base class for all valve devices. | ||||
|  * | ||||
|  * Valves currently have three properties: | ||||
|  *  - position - The current position of the valve from 0.0 (fully closed) to 1.0 (fully open). | ||||
|  *    For valves with only binary OPEN/CLOSED position this will always be either 0.0 or 1.0 | ||||
|  *  - current_operation - The operation the valve is currently performing, this can | ||||
|  *    be one of IDLE, OPENING and CLOSING. | ||||
|  * | ||||
|  * For users: All valve operations must be performed over the .make_call() interface. | ||||
|  * To command a valve, use .make_call() to create a call object, set all properties | ||||
|  * you wish to set, and activate the command with .perform(). | ||||
|  * For reading out the current values of the valve, use the public .position, etc. | ||||
|  * properties (these are read-only for users) | ||||
|  * | ||||
|  * For integrations: Integrations must implement two methods: control() and get_traits(). | ||||
|  * Control will be called with the arguments supplied by the user and should be used | ||||
|  * to control all values of the valve. Also implement get_traits() to return what operations | ||||
|  * the valve supports. | ||||
|  */ | ||||
| class Valve : public EntityBase, public EntityBase_DeviceClass { | ||||
|  public: | ||||
|   explicit Valve(); | ||||
|  | ||||
|   /// The current operation of the valve (idle, opening, closing). | ||||
|   ValveOperation current_operation{VALVE_OPERATION_IDLE}; | ||||
|   /** The position of the valve from 0.0 (fully closed) to 1.0 (fully open). | ||||
|    * | ||||
|    * For binary valves this is always equals to 0.0 or 1.0 (see also VALVE_OPEN and | ||||
|    * VALVE_CLOSED constants). | ||||
|    */ | ||||
|   float position; | ||||
|  | ||||
|   /// Construct a new valve call used to control the valve. | ||||
|   ValveCall make_call(); | ||||
|  | ||||
|   void add_on_state_callback(std::function<void()> &&f); | ||||
|  | ||||
|   /** Publish the current state of the valve. | ||||
|    * | ||||
|    * First set the .position, etc. values and then call this method | ||||
|    * to publish the state of the valve. | ||||
|    * | ||||
|    * @param save Whether to save the updated values in RTC area. | ||||
|    */ | ||||
|   void publish_state(bool save = true); | ||||
|  | ||||
|   virtual ValveTraits get_traits() = 0; | ||||
|  | ||||
|   /// Helper method to check if the valve is fully open. Equivalent to comparing .position against 1.0 | ||||
|   bool is_fully_open() const; | ||||
|   /// Helper method to check if the valve is fully closed. Equivalent to comparing .position against 0.0 | ||||
|   bool is_fully_closed() const; | ||||
|  | ||||
|  protected: | ||||
|   friend ValveCall; | ||||
|  | ||||
|   virtual void control(const ValveCall &call) = 0; | ||||
|  | ||||
|   optional<ValveRestoreState> restore_state_(); | ||||
|  | ||||
|   CallbackManager<void()> state_callback_{}; | ||||
|  | ||||
|   ESPPreferenceObject rtc_; | ||||
| }; | ||||
|  | ||||
| }  // namespace valve | ||||
| }  // namespace esphome | ||||
							
								
								
									
										27
									
								
								esphome/components/valve/valve_traits.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/valve/valve_traits.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #pragma once | ||||
|  | ||||
| namespace esphome { | ||||
| namespace valve { | ||||
|  | ||||
| class ValveTraits { | ||||
|  public: | ||||
|   ValveTraits() = default; | ||||
|  | ||||
|   bool get_is_assumed_state() const { return this->is_assumed_state_; } | ||||
|   void set_is_assumed_state(bool is_assumed_state) { this->is_assumed_state_ = is_assumed_state; } | ||||
|   bool get_supports_position() const { return this->supports_position_; } | ||||
|   void set_supports_position(bool supports_position) { this->supports_position_ = supports_position; } | ||||
|   bool get_supports_toggle() const { return this->supports_toggle_; } | ||||
|   void set_supports_toggle(bool supports_toggle) { this->supports_toggle_ = supports_toggle; } | ||||
|   bool get_supports_stop() const { return this->supports_stop_; } | ||||
|   void set_supports_stop(bool supports_stop) { this->supports_stop_ = supports_stop; } | ||||
|  | ||||
|  protected: | ||||
|   bool is_assumed_state_{false}; | ||||
|   bool supports_position_{false}; | ||||
|   bool supports_toggle_{false}; | ||||
|   bool supports_stop_{false}; | ||||
| }; | ||||
|  | ||||
| }  // namespace valve | ||||
| }  // namespace esphome | ||||
| @@ -86,6 +86,15 @@ bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_VALVE | ||||
| bool ListEntitiesIterator::on_valve(valve::Valve *valve) { | ||||
|   if (this->web_server_->events_.count() == 0) | ||||
|     return true; | ||||
|   this->web_server_->events_.send(this->web_server_->valve_json(valve, DETAIL_ALL).c_str(), "state"); | ||||
|   return true; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_CLIMATE | ||||
| bool ListEntitiesIterator::on_climate(climate::Climate *climate) { | ||||
|   if (this->web_server_->events_.count() == 0) | ||||
| @@ -150,5 +159,14 @@ bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
| bool ListEntitiesIterator::on_event(event::Event *event) { | ||||
|   // Null event type, since we are just iterating over entities | ||||
|   const std::string null_event_type = ""; | ||||
|   this->web_server_->events_.send(this->web_server_->event_json(event, null_event_type, DETAIL_ALL).c_str(), "state"); | ||||
|   return true; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| }  // namespace web_server | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -56,9 +56,15 @@ class ListEntitiesIterator : public ComponentIterator { | ||||
| #ifdef USE_LOCK | ||||
|   bool on_lock(lock::Lock *a_lock) override; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   bool on_valve(valve::Valve *valve) override; | ||||
| #endif | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   bool on_event(event::Event *event) override; | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   WebServer *web_server_; | ||||
|   | ||||
| @@ -1260,6 +1260,68 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_VALVE | ||||
| void WebServer::on_valve_update(valve::Valve *obj) { | ||||
|   if (this->events_.count() == 0) | ||||
|     return; | ||||
|   this->events_.send(this->valve_json(obj, DETAIL_STATE).c_str(), "state"); | ||||
| } | ||||
| void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) { | ||||
|   for (valve::Valve *obj : App.get_valves()) { | ||||
|     if (obj->get_object_id() != match.id) | ||||
|       continue; | ||||
|  | ||||
|     if (request->method() == HTTP_GET && match.method.empty()) { | ||||
|       std::string data = this->valve_json(obj, DETAIL_STATE); | ||||
|       request->send(200, "application/json", data.c_str()); | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     auto call = obj->make_call(); | ||||
|     if (match.method == "open") { | ||||
|       call.set_command_open(); | ||||
|     } else if (match.method == "close") { | ||||
|       call.set_command_close(); | ||||
|     } else if (match.method == "stop") { | ||||
|       call.set_command_stop(); | ||||
|     } else if (match.method == "toggle") { | ||||
|       call.set_command_toggle(); | ||||
|     } else if (match.method != "set") { | ||||
|       request->send(404); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     auto traits = obj->get_traits(); | ||||
|     if (request->hasParam("position") && !traits.get_supports_position()) { | ||||
|       request->send(409); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (request->hasParam("position")) { | ||||
|       auto position = parse_number<float>(request->getParam("position")->value().c_str()); | ||||
|       if (position.has_value()) { | ||||
|         call.set_position(*position); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     this->schedule_([call]() mutable { call.perform(); }); | ||||
|     request->send(200); | ||||
|     return; | ||||
|   } | ||||
|   request->send(404); | ||||
| } | ||||
| std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) { | ||||
|   return json::build_json([obj, start_config](JsonObject root) { | ||||
|     set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN", | ||||
|                               obj->position, start_config); | ||||
|     root["current_operation"] = valve::valve_operation_to_str(obj->current_operation); | ||||
|  | ||||
|     if (obj->get_traits().get_supports_position()) | ||||
|       root["position"] = obj->position; | ||||
|   }); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
| void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) { | ||||
|   if (this->events_.count() == 0) | ||||
| @@ -1290,6 +1352,28 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
| void WebServer::on_event(event::Event *obj, const std::string &event_type) { | ||||
|   this->events_.send(this->event_json(obj, event_type, DETAIL_STATE).c_str(), "state"); | ||||
| } | ||||
|  | ||||
| std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) { | ||||
|   return json::build_json([obj, event_type, start_config](JsonObject root) { | ||||
|     set_json_id(root, obj, "event-" + obj->get_object_id(), start_config); | ||||
|     if (!event_type.empty()) { | ||||
|       root["event_type"] = event_type; | ||||
|     } | ||||
|     if (start_config == DETAIL_ALL) { | ||||
|       JsonArray event_types = root.createNestedArray("event_types"); | ||||
|       for (auto const &event_type : obj->get_event_types()) { | ||||
|         event_types.add(event_type); | ||||
|       } | ||||
|       root["device_class"] = obj->get_device_class(); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| bool WebServer::canHandle(AsyncWebServerRequest *request) { | ||||
|   if (request->url() == "/") | ||||
|     return true; | ||||
| @@ -1394,6 +1478,11 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { | ||||
|     return true; | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_VALVE | ||||
|   if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "valve") | ||||
|     return true; | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   if (request->method() == HTTP_GET && match.domain == "alarm_control_panel") | ||||
|     return true; | ||||
| @@ -1535,6 +1624,13 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_VALVE | ||||
|   if (match.domain == "valve") { | ||||
|     this->handle_valve_request(request, match); | ||||
|     return; | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   if (match.domain == "alarm_control_panel") { | ||||
|     this->handle_alarm_control_panel_request(request, match); | ||||
|   | ||||
| @@ -276,6 +276,16 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { | ||||
|   std::string lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config); | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_VALVE | ||||
|   void on_valve_update(valve::Valve *obj) override; | ||||
|  | ||||
|   /// Handle a valve request under '/valve/<id>/<open/close/stop/set>'. | ||||
|   void handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match); | ||||
|  | ||||
|   /// Dump the valve state as a JSON string. | ||||
|   std::string valve_json(valve::Valve *obj, JsonDetail start_config); | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override; | ||||
|  | ||||
| @@ -287,6 +297,13 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { | ||||
|                                        alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config); | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
|   void on_event(event::Event *obj, const std::string &event_type) override; | ||||
|  | ||||
|   /// Dump the event details with its value as a JSON string. | ||||
|   std::string event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config); | ||||
| #endif | ||||
|  | ||||
|   /// Override the web handler's canHandle method. | ||||
|   bool canHandle(AsyncWebServerRequest *request) override; | ||||
|   /// Override the web handler's handleRequest method. | ||||
|   | ||||
							
								
								
									
										108
									
								
								esphome/components/weikai/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								esphome/components/weikai/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import uart | ||||
| from esphome.const import ( | ||||
|     CONF_BAUD_RATE, | ||||
|     CONF_CHANNEL, | ||||
|     CONF_ID, | ||||
|     CONF_INPUT, | ||||
|     CONF_INVERTED, | ||||
|     CONF_MODE, | ||||
|     CONF_NUMBER, | ||||
|     CONF_OUTPUT, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
| AUTO_LOAD = ["uart"] | ||||
|  | ||||
| MULTI_CONF = True | ||||
| CONF_STOP_BITS = "stop_bits" | ||||
| CONF_PARITY = "parity" | ||||
| CONF_CRYSTAL = "crystal" | ||||
| CONF_UART = "uart" | ||||
| CONF_TEST_MODE = "test_mode" | ||||
|  | ||||
| weikai_ns = cg.esphome_ns.namespace("weikai") | ||||
| WeikaiComponent = weikai_ns.class_("WeikaiComponent", cg.Component) | ||||
| WeikaiChannel = weikai_ns.class_("WeikaiChannel", uart.UARTComponent) | ||||
|  | ||||
|  | ||||
| def check_channel_max(value, max): | ||||
|     channel_uniq = [] | ||||
|     channel_dup = [] | ||||
|     for x in value[CONF_UART]: | ||||
|         if x[CONF_CHANNEL] > max - 1: | ||||
|             raise cv.Invalid(f"Invalid channel number: {x[CONF_CHANNEL]}") | ||||
|         if x[CONF_CHANNEL] not in channel_uniq: | ||||
|             channel_uniq.append(x[CONF_CHANNEL]) | ||||
|         else: | ||||
|             channel_dup.append(x[CONF_CHANNEL]) | ||||
|     if len(channel_dup) > 0: | ||||
|         raise cv.Invalid(f"Duplicate channel list: {channel_dup}") | ||||
|     return value | ||||
|  | ||||
|  | ||||
| def check_channel_max_4(value): | ||||
|     return check_channel_max(value, 4) | ||||
|  | ||||
|  | ||||
| def check_channel_max_2(value): | ||||
|     return check_channel_max(value, 2) | ||||
|  | ||||
|  | ||||
| WKBASE_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(WeikaiComponent), | ||||
|         cv.Optional(CONF_CRYSTAL, default=14745600): cv.int_, | ||||
|         cv.Optional(CONF_TEST_MODE, default=0): cv.int_, | ||||
|         cv.Required(CONF_UART): cv.ensure_list( | ||||
|             { | ||||
|                 cv.Required(CONF_ID): cv.declare_id(WeikaiChannel), | ||||
|                 cv.Optional(CONF_CHANNEL, default=0): cv.int_range(min=0, max=3), | ||||
|                 cv.Required(CONF_BAUD_RATE): cv.int_range(min=1), | ||||
|                 cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True), | ||||
|                 cv.Optional(CONF_PARITY, default="NONE"): cv.enum( | ||||
|                     uart.UART_PARITY_OPTIONS, upper=True | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|     } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
|  | ||||
| async def register_weikai(var, config): | ||||
|     """Register an weikai device with the given config.""" | ||||
|     cg.add(var.set_crystal(config[CONF_CRYSTAL])) | ||||
|     cg.add(var.set_test_mode(config[CONF_TEST_MODE])) | ||||
|     await cg.register_component(var, config) | ||||
|     for uart_elem in config[CONF_UART]: | ||||
|         chan = cg.new_Pvariable(uart_elem[CONF_ID]) | ||||
|         cg.add(chan.set_channel_name(str(uart_elem[CONF_ID]))) | ||||
|         cg.add(chan.set_parent(var)) | ||||
|         cg.add(chan.set_channel(uart_elem[CONF_CHANNEL])) | ||||
|         cg.add(chan.set_baud_rate(uart_elem[CONF_BAUD_RATE])) | ||||
|         cg.add(chan.set_stop_bits(uart_elem[CONF_STOP_BITS])) | ||||
|         cg.add(chan.set_parity(uart_elem[CONF_PARITY])) | ||||
|  | ||||
|  | ||||
| def validate_pin_mode(value): | ||||
|     """Checks input/output mode inconsistency""" | ||||
|     if not (value[CONF_MODE][CONF_INPUT] or value[CONF_MODE][CONF_OUTPUT]): | ||||
|         raise cv.Invalid("Mode must be either input or output") | ||||
|     if value[CONF_MODE][CONF_INPUT] and value[CONF_MODE][CONF_OUTPUT]: | ||||
|         raise cv.Invalid("Mode must be either input or output") | ||||
|     return value | ||||
|  | ||||
|  | ||||
| WEIKAI_PIN_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_NUMBER): cv.int_range(min=0, max=7), | ||||
|         cv.Optional(CONF_MODE, default={}): cv.All( | ||||
|             { | ||||
|                 cv.Optional(CONF_INPUT, default=False): cv.boolean, | ||||
|                 cv.Optional(CONF_OUTPUT, default=False): cv.boolean, | ||||
|             }, | ||||
|         ), | ||||
|         cv.Optional(CONF_INVERTED, default=False): cv.boolean, | ||||
|     } | ||||
| ) | ||||
							
								
								
									
										615
									
								
								esphome/components/weikai/weikai.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										615
									
								
								esphome/components/weikai/weikai.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,615 @@ | ||||
| /// @file weikai.cpp | ||||
| /// @brief  WeiKai component family - classes implementation | ||||
| /// @date Last Modified: 2024/04/06 15:13:11 | ||||
| /// @details The classes declared in this file can be used by the Weikai family | ||||
|  | ||||
| #include "weikai.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace weikai { | ||||
|  | ||||
| /*! @mainpage Weikai source code documentation | ||||
|  This documentation provides information about the implementation of the family of WeiKai Components in ESPHome. | ||||
|  Here is the class diagram related to Weikai family of components: | ||||
|  @image html weikai_class.png | ||||
|  | ||||
|   @section WKRingBuffer_ The WKRingBuffer template class | ||||
| The WKRingBuffer template class has it names implies implement a simple ring buffer helper class. This straightforward | ||||
| container implements FIFO functionality, enabling bytes to be pushed into one side and popped from the other in the | ||||
| order of entry. Implementation is classic and therefore not described in any details. | ||||
|  | ||||
|   @section WeikaiRegister_ The WeikaiRegister class | ||||
|  The WeikaiRegister helper class creates objects that act as proxies to the device registers. | ||||
|  @details This is an abstract virtual class (interface) that provides all the necessary access to registers while hiding | ||||
|  the actual implementation. The access to the registers can be made through an I²C bus in for example for wk2168_i2c | ||||
|  component or through a SPI bus for example in the case of the wk2168_spi component. Derived classes will actually | ||||
|  performs the specific bus operations. | ||||
|  | ||||
|  @section WeikaiRegisterI2C_ WeikaiRegisterI2C | ||||
|  The weikai_i2c::WeikaiRegisterI2C class implements the virtual methods of the WeikaiRegister class for an I2C bus. | ||||
|  | ||||
|   @section WeikaiRegisterSPI_ WeikaiRegisterSPI | ||||
|  The weikai_spi::WeikaiRegisterSPI class implements the virtual methods of the WeikaiRegister class for an SPI bus. | ||||
|  | ||||
|  @section WeikaiComponent_ The WeikaiComponent class | ||||
| The WeikaiComponent class stores the information global to a WeiKai family component and provides methods to set/access | ||||
| this information. It also serves as a container for WeikaiChannel instances. This is done by maintaining an array of | ||||
| references these WeikaiChannel instances. This class derives from the esphome::Component classes. This class override | ||||
| esphome::Component::loop() method to facilitate the seamless transfer of accumulated bytes from the receive | ||||
| FIFO into the ring buffer. This process ensures quick access to the stored bytes, enhancing the overall efficiency of | ||||
| the component. | ||||
|  | ||||
|  @section WeikaiComponentI2C_ WeikaiComponentI2C | ||||
|  The weikai_i2c::WeikaiComponentI2C class implements the virtual methods of the WeikaiComponent class for an I2C bus. | ||||
|  | ||||
|   @section WeikaiComponentSPI_ WeikaiComponentSPI | ||||
|  The weikai_spi::WeikaiComponentSPI class implements the virtual methods of the WeikaiComponent class for an SPI bus. | ||||
|  | ||||
|  @section WeikaiGPIOPin_ WeikaiGPIOPin class | ||||
|  The WeikaiGPIOPin class is an helper class to expose the GPIO pins of WK family components as if they were internal | ||||
|  GPIO pins. It also provides the setup() and dump_summary() methods. | ||||
|  | ||||
|  @section WeikaiChannel_ The WeikaiChannel class | ||||
|  The WeikaiChannel class is used to implement all the virtual methods of the ESPHome uart::UARTComponent class. An | ||||
|  individual instance of this class is created for each UART channel. It has a link back to the WeikaiComponent object it | ||||
|  belongs to. This class derives from the uart::UARTComponent class. It collaborates through an aggregation with | ||||
|  WeikaiComponent. This implies that WeikaiComponent acts as a container, housing several WeikaiChannel instances. | ||||
|  Furthermore, the WeikaiChannel class derives from the ESPHome uart::UARTComponent class, it also has an association | ||||
|  relationship with the WKRingBuffer and WeikaiRegister helper classes. Consequently, when a WeikaiChannel instance is | ||||
|  destroyed, the associated WKRingBuffer instance is also destroyed. | ||||
|  | ||||
| */ | ||||
|  | ||||
| static const char *const TAG = "weikai"; | ||||
|  | ||||
| /// @brief convert an int to binary representation as C++ std::string | ||||
| /// @param val integer to convert | ||||
| /// @return a std::string | ||||
| inline std::string i2s(uint8_t val) { return std::bitset<8>(val).to_string(); } | ||||
| /// Convert std::string to C string | ||||
| #define I2S2CS(val) (i2s(val).c_str()) | ||||
|  | ||||
| /// @brief measure the time elapsed between two calls | ||||
| /// @param last_time time of the previous call | ||||
| /// @return the elapsed time in milliseconds | ||||
| uint32_t elapsed_ms(uint32_t &last_time) { | ||||
|   uint32_t e = millis() - last_time; | ||||
|   last_time = millis(); | ||||
|   return e; | ||||
| }; | ||||
|  | ||||
| /// @brief Converts the parity enum value to a C string | ||||
| /// @param parity enum | ||||
| /// @return the string | ||||
| const char *p2s(uart::UARTParityOptions parity) { | ||||
|   using namespace uart; | ||||
|   switch (parity) { | ||||
|     case UART_CONFIG_PARITY_NONE: | ||||
|       return "NONE"; | ||||
|     case UART_CONFIG_PARITY_EVEN: | ||||
|       return "EVEN"; | ||||
|     case UART_CONFIG_PARITY_ODD: | ||||
|       return "ODD"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// @brief Display a buffer in hexadecimal format (32 hex values / line) for debug | ||||
| void print_buffer(const uint8_t *data, size_t length) { | ||||
|   char hex_buffer[100]; | ||||
|   hex_buffer[(3 * 32) + 1] = 0; | ||||
|   for (size_t i = 0; i < length; i++) { | ||||
|     snprintf(&hex_buffer[3 * (i % 32)], sizeof(hex_buffer), "%02X ", data[i]); | ||||
|     if (i % 32 == 31) { | ||||
|       ESP_LOGVV(TAG, "   %s", hex_buffer); | ||||
|     } | ||||
|   } | ||||
|   if (length % 32) { | ||||
|     // null terminate if incomplete line | ||||
|     hex_buffer[3 * (length % 32) + 2] = 0; | ||||
|     ESP_LOGVV(TAG, "   %s", hex_buffer); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static const char *const REG_TO_STR_P0[16] = {"GENA", "GRST",  "GMUT",  "SPAGE", "SCR", "LCR",  "FCR",  "SIER", | ||||
|                                               "SIFR", "TFCNT", "RFCNT", "FSR",   "LSR", "FDAT", "FWCR", "RS485"}; | ||||
| static const char *const REG_TO_STR_P1[16] = {"GENA", "GRST", "GMUT", "SPAGE", "BAUD1", "BAUD0", "PRES", "RFTL", | ||||
|                                               "TFTL", "FWTH", "FWTL", "XON1",  "XOFF1", "SADR",  "SAEN", "RTSDLY"}; | ||||
|  | ||||
| // method to print a register value as text: used in the log messages ... | ||||
| const char *reg_to_str(int reg, bool page1) { | ||||
|   if (reg == WKREG_GPDAT) { | ||||
|     return "GPDAT"; | ||||
|   } else if (reg == WKREG_GPDIR) { | ||||
|     return "GPDIR"; | ||||
|   } else { | ||||
|     return page1 ? REG_TO_STR_P1[reg & 0x0F] : REG_TO_STR_P0[reg & 0x0F]; | ||||
|   } | ||||
| } | ||||
|  | ||||
| enum RegType { REG = 0, FIFO = 1 };  ///< Register or FIFO | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| // The WeikaiRegister methods | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| WeikaiRegister &WeikaiRegister::operator=(uint8_t value) { | ||||
|   write_reg(value); | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| WeikaiRegister &WeikaiRegister::operator&=(uint8_t value) { | ||||
|   value &= read_reg(); | ||||
|   write_reg(value); | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| WeikaiRegister &WeikaiRegister::operator|=(uint8_t value) { | ||||
|   value |= read_reg(); | ||||
|   write_reg(value); | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| // The WeikaiComponent methods | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| void WeikaiComponent::loop() { | ||||
|   if ((this->component_state_ & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP) | ||||
|     return; | ||||
|  | ||||
|   // If there are some bytes in the receive FIFO we transfers them to the ring buffers | ||||
|   size_t transferred = 0; | ||||
|   for (auto *child : this->children_) { | ||||
|     // we look if some characters has been received in the fifo | ||||
|     transferred += child->xfer_fifo_to_buffer_(); | ||||
|   } | ||||
|   if (transferred > 0) { | ||||
|     ESP_LOGV(TAG, "we transferred %d bytes from fifo to buffer...", transferred); | ||||
|   } | ||||
|  | ||||
| #ifdef TEST_COMPONENT | ||||
|   static uint32_t loop_time = 0; | ||||
|   static uint32_t loop_count = 0; | ||||
|   uint32_t time = 0; | ||||
|  | ||||
|   if (test_mode_ == 1) {  // test component in loopback | ||||
|     ESP_LOGI(TAG, "Component loop %" PRIu32 " for %s : %" PRIu32 " ms since last call ...", loop_count++, | ||||
|              this->get_name(), millis() - loop_time); | ||||
|     loop_time = millis(); | ||||
|     char message[64]; | ||||
|     elapsed_ms(time);  // set time to now | ||||
|     for (int i = 0; i < this->children_.size(); i++) { | ||||
|       if (i != ((loop_count - 1) % this->children_.size()))  // we do only one per loop | ||||
|         continue; | ||||
|       snprintf(message, sizeof(message), "%s:%s", this->get_name(), children_[i]->get_channel_name()); | ||||
|       children_[i]->uart_send_test_(message); | ||||
|       uint32_t const start_time = millis(); | ||||
|       while (children_[i]->tx_fifo_is_not_empty_()) {  // wait until buffer empty | ||||
|         if (millis() - start_time > 1500) { | ||||
|           ESP_LOGE(TAG, "timeout while flushing - %d bytes left in buffer...", children_[i]->tx_in_fifo_()); | ||||
|           break; | ||||
|         } | ||||
|         yield();  // reschedule our thread to avoid blocking | ||||
|       } | ||||
|       bool status = children_[i]->uart_receive_test_(message); | ||||
|       ESP_LOGI(TAG, "Test %s => send/received %u bytes %s - execution time %" PRIu32 " ms...", message, | ||||
|                RING_BUFFER_SIZE, status ? "correctly" : "with error", elapsed_ms(time)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (this->test_mode_ == 2) {  // test component in echo mode | ||||
|     for (auto *child : this->children_) { | ||||
|       uint8_t data = 0; | ||||
|       if (child->available()) { | ||||
|         child->read_byte(&data); | ||||
|         ESP_LOGI(TAG, "echo mode: read -> send %02X", data); | ||||
|         child->write_byte(data); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   if (test_mode_ == 3) { | ||||
|     test_gpio_input_(); | ||||
|   } | ||||
|  | ||||
|   if (test_mode_ == 4) { | ||||
|     test_gpio_output_(); | ||||
|   } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| #if defined(TEST_COMPONENT) | ||||
| void WeikaiComponent::test_gpio_input_() { | ||||
|   static bool init_input{false}; | ||||
|   static uint8_t state{0}; | ||||
|   uint8_t value; | ||||
|   if (!init_input) { | ||||
|     init_input = true; | ||||
|     // set all pins in input mode | ||||
|     this->reg(WKREG_GPDIR, 0) = 0x00; | ||||
|     ESP_LOGI(TAG, "initializing all pins to input mode"); | ||||
|     state = this->reg(WKREG_GPDAT, 0); | ||||
|     ESP_LOGI(TAG, "initial input data state = %02X (%s)", state, I2S2CS(state)); | ||||
|   } | ||||
|   value = this->reg(WKREG_GPDAT, 0); | ||||
|   if (value != state) { | ||||
|     ESP_LOGI(TAG, "Input data changed from %02X to %02X (%s)", state, value, I2S2CS(value)); | ||||
|     state = value; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void WeikaiComponent::test_gpio_output_() { | ||||
|   static bool init_output{false}; | ||||
|   static uint8_t state{0}; | ||||
|   if (!init_output) { | ||||
|     init_output = true; | ||||
|     // set all pins in output mode | ||||
|     this->reg(WKREG_GPDIR, 0) = 0xFF; | ||||
|     ESP_LOGI(TAG, "initializing all pins to output mode"); | ||||
|     this->reg(WKREG_GPDAT, 0) = state; | ||||
|     ESP_LOGI(TAG, "setting all outputs to 0"); | ||||
|   } | ||||
|   state = ~state; | ||||
|   this->reg(WKREG_GPDAT, 0) = state; | ||||
|   ESP_LOGI(TAG, "Flipping all outputs to %02X (%s)", state, I2S2CS(state)); | ||||
|   delay(100);  // NOLINT | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| // The WeikaiGPIOPin methods | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| bool WeikaiComponent::read_pin_val_(uint8_t pin) { | ||||
|   this->input_state_ = this->reg(WKREG_GPDAT, 0); | ||||
|   ESP_LOGVV(TAG, "reading input pin %u = %u in_state %s", pin, this->input_state_ & (1 << pin), I2S2CS(input_state_)); | ||||
|   return this->input_state_ & (1 << pin); | ||||
| } | ||||
|  | ||||
| void WeikaiComponent::write_pin_val_(uint8_t pin, bool value) { | ||||
|   if (value) { | ||||
|     this->output_state_ |= (1 << pin); | ||||
|   } else { | ||||
|     this->output_state_ &= ~(1 << pin); | ||||
|   } | ||||
|   ESP_LOGVV(TAG, "writing output pin %d with %d out_state %s", pin, uint8_t(value), I2S2CS(this->output_state_)); | ||||
|   this->reg(WKREG_GPDAT, 0) = this->output_state_; | ||||
| } | ||||
|  | ||||
| void WeikaiComponent::set_pin_direction_(uint8_t pin, gpio::Flags flags) { | ||||
|   if (flags == gpio::FLAG_INPUT) { | ||||
|     this->pin_config_ &= ~(1 << pin);  // clear bit (input mode) | ||||
|   } else { | ||||
|     if (flags == gpio::FLAG_OUTPUT) { | ||||
|       this->pin_config_ |= 1 << pin;  // set bit (output mode) | ||||
|     } else { | ||||
|       ESP_LOGE(TAG, "pin %d direction invalid", pin); | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGVV(TAG, "setting pin %d direction to %d pin_config=%s", pin, flags, I2S2CS(this->pin_config_)); | ||||
|   this->reg(WKREG_GPDIR, 0) = this->pin_config_;  // TODO check ~ | ||||
| } | ||||
|  | ||||
| void WeikaiGPIOPin::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting GPIO pin %d mode to %s", this->pin_, | ||||
|                 flags_ == gpio::FLAG_INPUT          ? "Input" | ||||
|                 : this->flags_ == gpio::FLAG_OUTPUT ? "Output" | ||||
|                                                     : "NOT SPECIFIED"); | ||||
|   // ESP_LOGCONFIG(TAG, "Setting GPIO pins mode to '%s' %02X", I2S2CS(this->flags_), this->flags_); | ||||
|   this->pin_mode(this->flags_); | ||||
| } | ||||
|  | ||||
| std::string WeikaiGPIOPin::dump_summary() const { | ||||
|   char buffer[32]; | ||||
|   snprintf(buffer, sizeof(buffer), "%u via WeiKai %s", this->pin_, this->parent_->get_name()); | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| // The WeikaiChannel methods | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| void WeikaiChannel::setup_channel() { | ||||
|   ESP_LOGCONFIG(TAG, "  Setting up UART %s:%s ...", this->parent_->get_name(), this->get_channel_name()); | ||||
|   // we enable transmit and receive on this channel | ||||
|   if (this->check_channel_down()) { | ||||
|     ESP_LOGCONFIG(TAG, "  Error channel %s not working...", this->get_channel_name()); | ||||
|   } | ||||
|   this->reset_fifo_(); | ||||
|   this->receive_buffer_.clear(); | ||||
|   this->set_line_param_(); | ||||
|   this->set_baudrate_(); | ||||
| } | ||||
|  | ||||
| void WeikaiChannel::dump_channel() { | ||||
|   ESP_LOGCONFIG(TAG, "  UART %s ...", this->get_channel_name()); | ||||
|   ESP_LOGCONFIG(TAG, "    Baud rate: %" PRIu32 " Bd", this->baud_rate_); | ||||
|   ESP_LOGCONFIG(TAG, "    Data bits: %u", this->data_bits_); | ||||
|   ESP_LOGCONFIG(TAG, "    Stop bits: %u", this->stop_bits_); | ||||
|   ESP_LOGCONFIG(TAG, "    Parity: %s", p2s(this->parity_)); | ||||
| } | ||||
|  | ||||
| void WeikaiChannel::reset_fifo_() { | ||||
|   // enable transmission and reception | ||||
|   this->reg(WKREG_SCR) = SCR_RXEN | SCR_TXEN; | ||||
|   // we reset and enable transmit and receive FIFO | ||||
|   this->reg(WKREG_FCR) = FCR_TFEN | FCR_RFEN | FCR_TFRST | FCR_RFRST; | ||||
| } | ||||
|  | ||||
| void WeikaiChannel::set_line_param_() { | ||||
|   this->data_bits_ = 8;  // always equal to 8 for WeiKai (cant be changed) | ||||
|   uint8_t lcr = 0; | ||||
|   if (this->stop_bits_ == 2) | ||||
|     lcr |= LCR_STPL; | ||||
|   switch (this->parity_) {  // parity selection settings | ||||
|     case uart::UART_CONFIG_PARITY_ODD: | ||||
|       lcr |= (LCR_PAEN | LCR_PAR_ODD); | ||||
|       break; | ||||
|     case uart::UART_CONFIG_PARITY_EVEN: | ||||
|       lcr |= (LCR_PAEN | LCR_PAR_EVEN); | ||||
|       break; | ||||
|     default: | ||||
|       break;  // no parity 000x | ||||
|   } | ||||
|   this->reg(WKREG_LCR) = lcr;  // write LCR | ||||
|   ESP_LOGV(TAG, "    line config: %d data_bits, %d stop_bits, parity %s register [%s]", this->data_bits_, | ||||
|            this->stop_bits_, p2s(this->parity_), I2S2CS(lcr)); | ||||
| } | ||||
|  | ||||
| void WeikaiChannel::set_baudrate_() { | ||||
|   if (this->baud_rate_ > this->parent_->crystal_ / 16) { | ||||
|     baud_rate_ = this->parent_->crystal_ / 16; | ||||
|     ESP_LOGE(TAG, " Requested baudrate too high for crystal=%" PRIu32 " Hz. Has been reduced to %" PRIu32 " Bd", | ||||
|              this->parent_->crystal_, this->baud_rate_); | ||||
|   }; | ||||
|   uint16_t const val_int = this->parent_->crystal_ / (this->baud_rate_ * 16) - 1; | ||||
|   uint16_t val_dec = (this->parent_->crystal_ % (this->baud_rate_ * 16)) / (this->baud_rate_ * 16); | ||||
|   uint8_t const baud_high = (uint8_t) (val_int >> 8); | ||||
|   uint8_t const baud_low = (uint8_t) (val_int & 0xFF); | ||||
|   while (val_dec > 0x0A) | ||||
|     val_dec /= 0x0A; | ||||
|   uint8_t const baud_dec = (uint8_t) (val_dec); | ||||
|  | ||||
|   this->parent_->page1_ = true;  // switch to page 1 | ||||
|   this->reg(WKREG_SPAGE) = 1; | ||||
|   this->reg(WKREG_BRH) = baud_high; | ||||
|   this->reg(WKREG_BRL) = baud_low; | ||||
|   this->reg(WKREG_BRD) = baud_dec; | ||||
|   this->parent_->page1_ = false;  // switch back to page 0 | ||||
|   this->reg(WKREG_SPAGE) = 0; | ||||
|  | ||||
|   ESP_LOGV(TAG, "    Crystal=%d baudrate=%d => registers [%d %d %d]", this->parent_->crystal_, this->baud_rate_, | ||||
|            baud_high, baud_low, baud_dec); | ||||
| } | ||||
|  | ||||
| inline bool WeikaiChannel::tx_fifo_is_not_empty_() { return this->reg(WKREG_FSR) & FSR_TFDAT; } | ||||
|  | ||||
| size_t WeikaiChannel::tx_in_fifo_() { | ||||
|   size_t tfcnt = this->reg(WKREG_TFCNT); | ||||
|   if (tfcnt == 0) { | ||||
|     uint8_t const fsr = this->reg(WKREG_FSR); | ||||
|     if (fsr & FSR_TFFULL) { | ||||
|       ESP_LOGVV(TAG, "tx FIFO full FSR=%s", I2S2CS(fsr)); | ||||
|       tfcnt = FIFO_SIZE; | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGVV(TAG, "tx FIFO contains %d bytes", tfcnt); | ||||
|   return tfcnt; | ||||
| } | ||||
|  | ||||
| size_t WeikaiChannel::rx_in_fifo_() { | ||||
|   size_t available = this->reg(WKREG_RFCNT); | ||||
|   uint8_t const fsr = this->reg(WKREG_FSR); | ||||
|   if (fsr & (FSR_RFOE | FSR_RFLB | FSR_RFFE | FSR_RFPE)) { | ||||
|     if (fsr & FSR_RFOE) | ||||
|       ESP_LOGE(TAG, "Receive data overflow FSR=%s", I2S2CS(fsr)); | ||||
|     if (fsr & FSR_RFLB) | ||||
|       ESP_LOGE(TAG, "Receive line break FSR=%s", I2S2CS(fsr)); | ||||
|     if (fsr & FSR_RFFE) | ||||
|       ESP_LOGE(TAG, "Receive frame error FSR=%s", I2S2CS(fsr)); | ||||
|     if (fsr & FSR_RFPE) | ||||
|       ESP_LOGE(TAG, "Receive parity error FSR=%s", I2S2CS(fsr)); | ||||
|   } | ||||
|   if ((available == 0) && (fsr & FSR_RFDAT)) { | ||||
|     // here we should be very careful because we can have something like this: | ||||
|     // -  at time t0 we read RFCNT=0 because nothing yet received | ||||
|     // -  at time t0+delta we might read FIFO not empty because one byte has just been received | ||||
|     // -  so to be sure we need to do another read of RFCNT and if it is still zero -> buffer full | ||||
|     available = this->reg(WKREG_RFCNT); | ||||
|     if (available == 0) {  // still zero ? | ||||
|       ESP_LOGV(TAG, "rx FIFO is full FSR=%s", I2S2CS(fsr)); | ||||
|       available = FIFO_SIZE; | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGVV(TAG, "rx FIFO contain %d bytes - FSR status=%s", available, I2S2CS(fsr)); | ||||
|   return available; | ||||
| } | ||||
|  | ||||
| bool WeikaiChannel::check_channel_down() { | ||||
|   // to check if we channel is up we write to the LCR W/R register | ||||
|   // note that this will put a break on the tx line for few ms | ||||
|   WeikaiRegister &lcr = this->reg(WKREG_LCR); | ||||
|   lcr = 0x3F; | ||||
|   uint8_t val = lcr; | ||||
|   if (val != 0x3F) { | ||||
|     ESP_LOGE(TAG, "R/W of register failed expected 0x3F received 0x%02X", val); | ||||
|     return true; | ||||
|   } | ||||
|   lcr = 0; | ||||
|   val = lcr; | ||||
|   if (val != 0x00) { | ||||
|     ESP_LOGE(TAG, "R/W of register failed expected 0x00 received 0x%02X", val); | ||||
|     return true; | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool WeikaiChannel::peek_byte(uint8_t *buffer) { | ||||
|   auto available = this->receive_buffer_.count(); | ||||
|   if (!available) | ||||
|     xfer_fifo_to_buffer_(); | ||||
|   return this->receive_buffer_.peek(*buffer); | ||||
| } | ||||
|  | ||||
| int WeikaiChannel::available() { | ||||
|   size_t available = this->receive_buffer_.count(); | ||||
|   if (!available) | ||||
|     available = xfer_fifo_to_buffer_(); | ||||
|   return available; | ||||
| } | ||||
|  | ||||
| bool WeikaiChannel::read_array(uint8_t *buffer, size_t length) { | ||||
|   bool status = true; | ||||
|   auto available = this->receive_buffer_.count(); | ||||
|   if (length > available) { | ||||
|     ESP_LOGW(TAG, "read_array: buffer underflow requested %d bytes only %d bytes available...", length, available); | ||||
|     length = available; | ||||
|     status = false; | ||||
|   } | ||||
|   // retrieve the bytes from ring buffer | ||||
|   for (size_t i = 0; i < length; i++) { | ||||
|     this->receive_buffer_.pop(buffer[i]); | ||||
|   } | ||||
|   ESP_LOGVV(TAG, "read_array(ch=%d buffer[0]=%02X, length=%d): status %s", this->channel_, *buffer, length, | ||||
|             status ? "OK" : "ERROR"); | ||||
|   return status; | ||||
| } | ||||
|  | ||||
| void WeikaiChannel::write_array(const uint8_t *buffer, size_t length) { | ||||
|   if (length > XFER_MAX_SIZE) { | ||||
|     ESP_LOGE(TAG, "Write_array: invalid call - requested %d bytes but max size %d ...", length, XFER_MAX_SIZE); | ||||
|     length = XFER_MAX_SIZE; | ||||
|   } | ||||
|   this->reg(0).write_fifo(const_cast<uint8_t *>(buffer), length); | ||||
| } | ||||
|  | ||||
| void WeikaiChannel::flush() { | ||||
|   uint32_t const start_time = millis(); | ||||
|   while (this->tx_fifo_is_not_empty_()) {  // wait until buffer empty | ||||
|     if (millis() - start_time > 200) { | ||||
|       ESP_LOGW(TAG, "WARNING flush timeout - still %d bytes not sent after 200 ms...", this->tx_in_fifo_()); | ||||
|       return; | ||||
|     } | ||||
|     yield();  // reschedule our thread to avoid blocking | ||||
|   } | ||||
| } | ||||
|  | ||||
| size_t WeikaiChannel::xfer_fifo_to_buffer_() { | ||||
|   size_t to_transfer; | ||||
|   size_t free; | ||||
|   while ((to_transfer = this->rx_in_fifo_()) && (free = this->receive_buffer_.free())) { | ||||
|     // while bytes in fifo and some room in the buffer we transfer | ||||
|     if (to_transfer > XFER_MAX_SIZE) | ||||
|       to_transfer = XFER_MAX_SIZE;  // we can only do so much | ||||
|     if (to_transfer > free) | ||||
|       to_transfer = free;  // we'll do the rest next time | ||||
|     if (to_transfer) { | ||||
|       uint8_t data[to_transfer]; | ||||
|       this->reg(0).read_fifo(data, to_transfer); | ||||
|       for (size_t i = 0; i < to_transfer; i++) | ||||
|         this->receive_buffer_.push(data[i]); | ||||
|     } | ||||
|   }  // while work to do | ||||
|   return to_transfer; | ||||
| } | ||||
|  | ||||
| /// | ||||
| // TEST COMPONENT | ||||
| // | ||||
| #ifdef TEST_COMPONENT | ||||
| /// @addtogroup test_ Test component information | ||||
| /// @{ | ||||
|  | ||||
| /// @brief An increment "Functor" (i.e. a class object that acts like a method with state!) | ||||
| /// | ||||
| /// Functors are objects that can be treated as though they are a function or function pointer. | ||||
| class Increment { | ||||
|  public: | ||||
|   /// @brief constructor: initialize current value to 0 | ||||
|   Increment() : i_(0) {} | ||||
|   /// @brief overload of the parenthesis operator. | ||||
|   /// Returns the current value and auto increment it | ||||
|   /// @return the current value. | ||||
|   uint8_t operator()() { return i_++; } | ||||
|  | ||||
|  private: | ||||
|   uint8_t i_; | ||||
| }; | ||||
|  | ||||
| /// @brief Hex converter to print/display a buffer in hexadecimal format (32 hex values / line). | ||||
| /// @param buffer contains the values to display | ||||
| void print_buffer(std::vector<uint8_t> buffer) { | ||||
|   char hex_buffer[100]; | ||||
|   hex_buffer[(3 * 32) + 1] = 0; | ||||
|   for (size_t i = 0; i < buffer.size(); i++) { | ||||
|     snprintf(&hex_buffer[3 * (i % 32)], sizeof(hex_buffer), "%02X ", buffer[i]); | ||||
|     if (i % 32 == 31) | ||||
|       ESP_LOGI(TAG, "   %s", hex_buffer); | ||||
|   } | ||||
|   if (buffer.size() % 32) { | ||||
|     // null terminate if incomplete line | ||||
|     hex_buffer[3 * (buffer.size() % 32) + 1] = 0; | ||||
|     ESP_LOGI(TAG, "   %s", hex_buffer); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// @brief test the write_array method | ||||
| void WeikaiChannel::uart_send_test_(char *message) { | ||||
|   auto start_exec = micros(); | ||||
|   std::vector<uint8_t> output_buffer(XFER_MAX_SIZE); | ||||
|   generate(output_buffer.begin(), output_buffer.end(), Increment());  // fill with incrementing number | ||||
|   size_t to_send = RING_BUFFER_SIZE; | ||||
|   while (to_send) { | ||||
|     this->write_array(&output_buffer[0], XFER_MAX_SIZE);  // we send the buffer | ||||
|     this->flush(); | ||||
|     to_send -= XFER_MAX_SIZE; | ||||
|   } | ||||
|   ESP_LOGV(TAG, "%s => sent %d bytes - exec time %d µs ...", message, RING_BUFFER_SIZE, micros() - start_exec); | ||||
| } | ||||
|  | ||||
| /// @brief test read_array method | ||||
| bool WeikaiChannel::uart_receive_test_(char *message) { | ||||
|   auto start_exec = micros(); | ||||
|   bool status = true; | ||||
|   size_t received = 0; | ||||
|   std::vector<uint8_t> buffer(RING_BUFFER_SIZE); | ||||
|  | ||||
|   // we wait until we have received all the bytes | ||||
|   uint32_t const start_time = millis(); | ||||
|   status = true; | ||||
|   while (received < RING_BUFFER_SIZE) { | ||||
|     while (XFER_MAX_SIZE > this->available()) { | ||||
|       this->xfer_fifo_to_buffer_(); | ||||
|       if (millis() - start_time > 1500) { | ||||
|         ESP_LOGE(TAG, "uart_receive_test_() timeout: only %d bytes received...", this->available()); | ||||
|         break; | ||||
|       } | ||||
|       yield();  // reschedule our thread to avoid blocking | ||||
|     } | ||||
|     status = this->read_array(&buffer[received], XFER_MAX_SIZE) && status; | ||||
|     received += XFER_MAX_SIZE; | ||||
|   } | ||||
|  | ||||
|   uint8_t peek_value = 0; | ||||
|   this->peek_byte(&peek_value); | ||||
|   if (peek_value != 0) { | ||||
|     ESP_LOGE(TAG, "Peek first byte value error..."); | ||||
|     status = false; | ||||
|   } | ||||
|  | ||||
|   for (size_t i = 0; i < RING_BUFFER_SIZE; i++) { | ||||
|     if (buffer[i] != i % XFER_MAX_SIZE) { | ||||
|       ESP_LOGE(TAG, "Read buffer contains error...b=%x i=%x", buffer[i], i % XFER_MAX_SIZE); | ||||
|       print_buffer(buffer); | ||||
|       status = false; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ESP_LOGV(TAG, "%s => received %d bytes  status %s - exec time %d µs ...", message, received, status ? "OK" : "ERROR", | ||||
|            micros() - start_exec); | ||||
|   return status; | ||||
| } | ||||
|  | ||||
| /// @} | ||||
| #endif | ||||
|  | ||||
| }  // namespace weikai | ||||
| }  // namespace esphome | ||||
							
								
								
									
										443
									
								
								esphome/components/weikai/weikai.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										443
									
								
								esphome/components/weikai/weikai.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,443 @@ | ||||
| /// @file weikai.h | ||||
| /// @author DrCoolZic | ||||
| /// @brief  WeiKai component family - classes declaration | ||||
| /// @date Last Modified: 2024/04/06 14:44:17 | ||||
| /// @details The classes declared in this file can be used by the Weikai family | ||||
| /// of UART and GPIO expander components. As of today it provides support for | ||||
| ///     wk2124_spi, wk2132_spi, wk2168_spi, wk2204_spi, wk2212_spi, | ||||
| ///                 wk2132_i2c, wk2168_i2c, wk2204_i2c, wk2212_i2c | ||||
|  | ||||
| #pragma once | ||||
| #include <bitset> | ||||
| #include <memory> | ||||
| #include <cinttypes> | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
| #include "wk_reg_def.h" | ||||
|  | ||||
| /// When the TEST_COMPONENT flag is defined we include some auto-test methods. Used to test the software during | ||||
| /// development but can also be used in situ to test if the component is working correctly. For release we do | ||||
| /// not set it by default but you can set it by using the following lines in you configuration file: | ||||
| /// @code | ||||
| /// esphome: | ||||
| ///   platformio_options: | ||||
| ///     build_flags: | ||||
| ///       - -DTEST_COMPONENT | ||||
| /// @endcode | ||||
| // #define TEST_COMPONENT | ||||
|  | ||||
| namespace esphome { | ||||
| namespace weikai { | ||||
|  | ||||
| /// @brief XFER_MAX_SIZE defines the maximum number of bytes allowed during one transfer. | ||||
| #if defined(I2C_BUFFER_LENGTH) | ||||
| constexpr size_t XFER_MAX_SIZE = I2C_BUFFER_LENGTH; | ||||
| #else | ||||
| constexpr size_t XFER_MAX_SIZE = 128; | ||||
| #endif | ||||
|  | ||||
| /// @brief size of the internal WeiKai FIFO | ||||
| constexpr size_t FIFO_SIZE = 256; | ||||
|  | ||||
| /// @brief size of the ring buffer set to size of the FIFO | ||||
| constexpr size_t RING_BUFFER_SIZE = FIFO_SIZE; | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| /// @brief This is an helper class that provides a simple ring buffers that works as a FIFO | ||||
| /// @details This ring buffer is used to buffer the bytes received in the FIFO of the Weika device. The best way to read | ||||
| /// characters from the device FIFO, is to first check how many bytes were received and then read them all at once. | ||||
| /// Unfortunately in all the code I have reviewed the characters are read one by one in a while loop by checking if | ||||
| /// bytes are available then reading the byte until no more byte available. This is pretty inefficient for two reasons: | ||||
| /// - Fist you need to perform a test for each byte to read | ||||
| /// - and second you call the read byte method for each character. | ||||
| /// . | ||||
| /// Assuming you need to read 100 bytes that results into 200 calls. This is to compare to 2 calls (one to find the | ||||
| /// number of bytes available plus one to read all the bytes) in the best case! If the registers you read are located on | ||||
| /// the micro-controller this is acceptable because the registers can be accessed fast. But when the registers are | ||||
| /// located on a remote device accessing them requires several cycles on a slow bus. As it it not possible to fix this | ||||
| /// problem by asking users to rewrite their code, I have implemented this ring buffer that store the bytes received | ||||
| /// locally. | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| template<typename T, size_t SIZE> class WKRingBuffer { | ||||
|  public: | ||||
|   /// @brief pushes an item at the tail of the fifo | ||||
|   /// @param item item to push | ||||
|   /// @return true if item has been pushed, false il item could not pushed (buffer full) | ||||
|   bool push(const T item) { | ||||
|     if (is_full()) | ||||
|       return false; | ||||
|     this->rb_[this->head_] = item; | ||||
|     this->head_ = (this->head_ + 1) % SIZE; | ||||
|     this->count_++; | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   /// @brief return and remove the item at head of the fifo | ||||
|   /// @param item item read | ||||
|   /// @return true if an item has been retrieved, false il no item available (buffer empty) | ||||
|   bool pop(T &item) { | ||||
|     if (is_empty()) | ||||
|       return false; | ||||
|     item = this->rb_[this->tail_]; | ||||
|     this->tail_ = (this->tail_ + 1) % SIZE; | ||||
|     this->count_--; | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   /// @brief return the value of the item at fifo's head without removing it | ||||
|   /// @param item pointer to item to return | ||||
|   /// @return true if item has been retrieved, false il no item available (buffer empty) | ||||
|   bool peek(T &item) { | ||||
|     if (is_empty()) | ||||
|       return false; | ||||
|     item = this->rb_[this->tail_]; | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   /// @brief test is the Ring Buffer is empty ? | ||||
|   /// @return true if empty | ||||
|   inline bool is_empty() { return (this->count_ == 0); } | ||||
|  | ||||
|   /// @brief test is the ring buffer is full ? | ||||
|   /// @return true if full | ||||
|   inline bool is_full() { return (this->count_ == SIZE); } | ||||
|  | ||||
|   /// @brief return the number of item in the ring buffer | ||||
|   /// @return the number of items | ||||
|   inline size_t count() { return this->count_; } | ||||
|  | ||||
|   /// @brief returns the number of free positions in the buffer | ||||
|   /// @return how many items can be added | ||||
|   inline size_t free() { return SIZE - this->count_; } | ||||
|  | ||||
|   /// @brief clear the buffer content | ||||
|   inline void clear() { this->head_ = this->tail_ = this->count_ = 0; } | ||||
|  | ||||
|  protected: | ||||
|   std::array<T, SIZE> rb_{0};  ///< the ring buffer | ||||
|   int tail_{0};                ///< position of the next element to read | ||||
|   int head_{0};                ///< position of the next element to write | ||||
|   size_t count_{0};            ///< count number of element in the buffer | ||||
| }; | ||||
|  | ||||
| class WeikaiComponent; | ||||
| // class WeikaiComponentSPI; | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| /// @brief WeikaiRegister objects acts as proxies to access remote register independently of the bus type. | ||||
| /// @details This is an abstract interface class that provides many operations to access to registers while hiding | ||||
| /// the actual implementation. This allow to accesses the registers in the Weikai component abstract class independently | ||||
| /// of the actual bus (I2C, SPI). The derived classes will actually implements the specific bus operations dependant of | ||||
| /// the bus used. | ||||
| /// @n typical usage of WeikaiRegister: | ||||
| /// @code | ||||
| ///   WeikaiRegister reg_X {&WeikaiComponent, ADDR_REGISTER_X, CHANNEL_NUM}  // declaration | ||||
| ///   reg_X |= 0x01;    // set bit 0 of the weikai register | ||||
| ///   reg_X &= ~0x01;   // reset bit 0 of the weikai register | ||||
| ///   reg_X = 10;       // Set the value of weikai register | ||||
| ///   uint val = reg_X; // get the value of weikai register | ||||
| /// @endcode | ||||
| class WeikaiRegister { | ||||
|  public: | ||||
|   /// @brief WeikaiRegister constructor. | ||||
|   /// @param comp our parent WeikaiComponent | ||||
|   /// @param reg address of the register | ||||
|   /// @param channel the channel of this register | ||||
|   WeikaiRegister(WeikaiComponent *const comp, uint8_t reg, uint8_t channel) | ||||
|       : comp_(comp), register_(reg), channel_(channel) {} | ||||
|   virtual ~WeikaiRegister() {} | ||||
|  | ||||
|   /// @brief overloads the = operator. This is used to set a value into the weikai register | ||||
|   /// @param value to be set | ||||
|   /// @return this object | ||||
|   WeikaiRegister &operator=(uint8_t value); | ||||
|  | ||||
|   /// @brief overloads the compound &= operator. This is often used to reset bits in the weikai register | ||||
|   /// @param value performs an & operation with value and store the result | ||||
|   /// @return this object | ||||
|   WeikaiRegister &operator&=(uint8_t value); | ||||
|  | ||||
|   /// @brief overloads the compound |= operator. This is often used to set bits in the weikai register | ||||
|   /// @param value performs an | operation with value and store the result | ||||
|   /// @return this object | ||||
|   WeikaiRegister &operator|=(uint8_t value); | ||||
|  | ||||
|   /// @brief cast operator that returns the content of the weikai register | ||||
|   operator uint8_t() const { return read_reg(); } | ||||
|  | ||||
|   /// @brief reads the register | ||||
|   /// @return the value read from the register | ||||
|   virtual uint8_t read_reg() const = 0; | ||||
|  | ||||
|   /// @brief writes the register | ||||
|   /// @param value to write in the register | ||||
|   virtual void write_reg(uint8_t value) = 0; | ||||
|  | ||||
|   /// @brief read an array of bytes from the receiver fifo | ||||
|   /// @param data pointer to data buffer | ||||
|   /// @param length number of bytes to read | ||||
|   virtual void read_fifo(uint8_t *data, size_t length) const = 0; | ||||
|  | ||||
|   /// @brief write an array of bytes to the transmitter fifo | ||||
|   /// @param data pointer to data buffer | ||||
|   /// @param length number of bytes to write | ||||
|   virtual void write_fifo(uint8_t *data, size_t length) = 0; | ||||
|  | ||||
|   WeikaiComponent *const comp_;  ///< pointer to our parent (aggregation) | ||||
|   uint8_t register_;             ///< address of the register | ||||
|   uint8_t channel_;              ///< channel for this register | ||||
| }; | ||||
|  | ||||
| class WeikaiChannel;  // forward declaration | ||||
| //////////////////////////////////////////////////////////////////////////////////// | ||||
| /// @brief The WeikaiComponent class stores the information global to the WeiKai component | ||||
| /// and provides methods to set/access this information. It is also the container of | ||||
| /// the WeikaiChannel children objects. This class is derived from esphome::Component | ||||
| /// class. | ||||
| //////////////////////////////////////////////////////////////////////////////////// | ||||
| class WeikaiComponent : public Component { | ||||
|  public: | ||||
|   /// @brief virtual destructor | ||||
|   virtual ~WeikaiComponent() {} | ||||
|  | ||||
|   /// @brief store crystal frequency | ||||
|   /// @param crystal frequency | ||||
|   void set_crystal(uint32_t crystal) { this->crystal_ = crystal; } | ||||
|  | ||||
|   /// @brief store if the component is in test mode | ||||
|   /// @param test_mode 0=normal mode any other values mean component in test mode | ||||
|   void set_test_mode(int test_mode) { this->test_mode_ = test_mode; } | ||||
|  | ||||
|   /// @brief store the name for the component | ||||
|   /// @param name the name as defined by the python code generator | ||||
|   void set_name(std::string name) { this->name_ = std::move(name); } | ||||
|  | ||||
|   /// @brief Get the name of the component | ||||
|   /// @return the name | ||||
|   const char *get_name() { return this->name_.c_str(); } | ||||
|  | ||||
|   /// @brief override the Component loop() | ||||
|   void loop() override; | ||||
|  | ||||
|   bool page1() { return page1_; } | ||||
|  | ||||
|   /// @brief Factory method to create a Register object | ||||
|   /// @param reg address of the register | ||||
|   /// @param channel channel associated with this register | ||||
|   /// @return a reference to WeikaiRegister | ||||
|   virtual WeikaiRegister ®(uint8_t reg, uint8_t channel) = 0; | ||||
|  | ||||
|  protected: | ||||
|   friend class WeikaiChannel; | ||||
|  | ||||
|   /// @brief Get the priority of the component | ||||
|   /// @return the priority | ||||
|   /// @details The priority is set  below setup_priority::BUS because we use | ||||
|   /// the spi/i2c busses (which has a priority of BUS) to communicate and the WeiKai | ||||
|   /// therefore it is seen by our client almost as if it was a bus. | ||||
|   float get_setup_priority() const override { return setup_priority::BUS - 0.1F; } | ||||
|  | ||||
|   friend class WeikaiGPIOPin; | ||||
|   /// Helper method to read the value of a pin. | ||||
|   bool read_pin_val_(uint8_t pin); | ||||
|  | ||||
|   /// Helper method to write the value of a pin. | ||||
|   void write_pin_val_(uint8_t pin, bool value); | ||||
|  | ||||
|   /// Helper method to set the pin mode of a pin. | ||||
|   void set_pin_direction_(uint8_t pin, gpio::Flags flags); | ||||
|  | ||||
| #ifdef TEST_COMPONENT | ||||
|   /// @defgroup test_ Test component information | ||||
|   /// @brief Contains information about the auto-tests of the component | ||||
|   /// @{ | ||||
|   void test_gpio_input_(); | ||||
|   void test_gpio_output_(); | ||||
|   /// @} | ||||
| #endif | ||||
|  | ||||
|   uint8_t pin_config_{0x00};                 ///< pin config mask: 1 means OUTPUT, 0 means INPUT | ||||
|   uint8_t output_state_{0x00};               ///< output state: 1 means HIGH, 0 means LOW | ||||
|   uint8_t input_state_{0x00};                ///< input pin states: 1 means HIGH, 0 means LOW | ||||
|   uint32_t crystal_;                         ///< crystal value; | ||||
|   int test_mode_;                            ///< test mode value (0 -> no tests) | ||||
|   bool page1_{false};                        ///< set to true when in "page1 mode" | ||||
|   std::vector<WeikaiChannel *> children_{};  ///< the list of WeikaiChannel UART children | ||||
|   std::string name_;                         ///< name of entity | ||||
| }; | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| /// @brief Helper class to expose a WeiKai family IO pin as an internal GPIO pin. | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| class WeikaiGPIOPin : public GPIOPin { | ||||
|  public: | ||||
|   void set_parent(WeikaiComponent *parent) { this->parent_ = parent; } | ||||
|   void set_pin(uint8_t pin) { this->pin_ = pin; } | ||||
|   void set_inverted(bool inverted) { this->inverted_ = inverted; } | ||||
|   void set_flags(gpio::Flags flags) { this->flags_ = flags; } | ||||
|  | ||||
|   void setup() override; | ||||
|   std::string dump_summary() const override; | ||||
|   void pin_mode(gpio::Flags flags) override { this->parent_->set_pin_direction_(this->pin_, flags); } | ||||
|   bool digital_read() override { return this->parent_->read_pin_val_(this->pin_) != this->inverted_; } | ||||
|   void digital_write(bool value) override { this->parent_->write_pin_val_(this->pin_, value != this->inverted_); } | ||||
|  | ||||
|  protected: | ||||
|   WeikaiComponent *parent_{nullptr}; | ||||
|   uint8_t pin_; | ||||
|   bool inverted_; | ||||
|   gpio::Flags flags_; | ||||
| }; | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| /// @brief The WeikaiChannel class is used to implement all the virtual methods of the ESPHome | ||||
| /// uart::UARTComponent virtual class. This class is common to the different members of the Weikai | ||||
| /// components family and therefore avoid code duplication. | ||||
| /////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| class WeikaiChannel : public uart::UARTComponent { | ||||
|  public: | ||||
|   /// @brief We belongs to this WeikaiComponent | ||||
|   /// @param parent pointer to the component we belongs to | ||||
|   void set_parent(WeikaiComponent *parent) { | ||||
|     this->parent_ = parent; | ||||
|     this->parent_->children_.push_back(this);  // add ourself to the list (vector) | ||||
|   } | ||||
|  | ||||
|   /// @brief Sets the channel number | ||||
|   /// @param channel number | ||||
|   void set_channel(uint8_t channel) { this->channel_ = channel; } | ||||
|  | ||||
|   /// @brief The name as generated by the Python code generator | ||||
|   /// @param name of the channel | ||||
|   void set_channel_name(std::string name) { this->name_ = std::move(name); } | ||||
|  | ||||
|   /// @brief Get the channel name | ||||
|   /// @return the name | ||||
|   const char *get_channel_name() { return this->name_.c_str(); } | ||||
|  | ||||
|   /// @brief Setup the channel | ||||
|   void virtual setup_channel(); | ||||
|  | ||||
|   /// @brief dump channel information | ||||
|   void virtual dump_channel(); | ||||
|  | ||||
|   /// @brief Factory method to create a WeikaiRegister proxy object | ||||
|   /// @param reg address of the register | ||||
|   /// @return a reference to WeikaiRegister | ||||
|   WeikaiRegister ®(uint8_t reg) { return this->parent_->reg(reg, channel_); } | ||||
|  | ||||
|   // | ||||
|   // we implements/overrides the virtual class from UARTComponent | ||||
|   // | ||||
|  | ||||
|   /// @brief Writes a specified number of bytes to a serial port | ||||
|   /// @param buffer pointer to the buffer | ||||
|   /// @param length number of bytes to write | ||||
|   /// @details This method sends 'length' characters from the buffer to the serial line. Unfortunately (unlike the | ||||
|   /// Arduino equivalent) this method does not return any flag and therefore it is not possible to know if any/all bytes | ||||
|   /// have been transmitted correctly. Another problem is that it is not possible to know ahead of time how many bytes | ||||
|   /// we can safely send as there is no tx_available() method provided! To avoid overrun when using the write method you | ||||
|   /// can use the flush() method to wait until the transmit fifo is empty. | ||||
|   /// @n Typical usage could be: | ||||
|   /// @code | ||||
|   ///   // ... | ||||
|   ///   uint8_t buffer[128]; | ||||
|   ///   // ... | ||||
|   ///   write_array(&buffer, length); | ||||
|   ///   flush(); | ||||
|   ///   // ... | ||||
|   /// @endcode | ||||
|   void write_array(const uint8_t *buffer, size_t length) override; | ||||
|  | ||||
|   /// @brief Reads a specified number of bytes from a serial port | ||||
|   /// @param buffer buffer to store the bytes | ||||
|   /// @param length number of bytes to read | ||||
|   /// @return true if succeed, false otherwise | ||||
|   /// @details Typical usage: | ||||
|   /// @code | ||||
|   ///   // ... | ||||
|   ///   auto length = available(); | ||||
|   ///   uint8_t buffer[128]; | ||||
|   ///   if (length > 0) { | ||||
|   ///     auto status = read_array(&buffer, length) | ||||
|   ///     // test status ... | ||||
|   ///   } | ||||
|   /// @endcode | ||||
|   bool read_array(uint8_t *buffer, size_t length) override; | ||||
|  | ||||
|   /// @brief Reads the first byte in FIFO without removing it | ||||
|   /// @param buffer pointer to the byte | ||||
|   /// @return true if succeed reading one byte, false if no character available | ||||
|   /// @details This method returns the next byte from receiving buffer without removing it from the internal fifo. It | ||||
|   /// returns true if a character is available and has been read, false otherwise.\n | ||||
|   bool peek_byte(uint8_t *buffer) override; | ||||
|  | ||||
|   /// @brief Returns the number of bytes in the receive buffer | ||||
|   /// @return the number of bytes available in the receiver fifo | ||||
|   int available() override; | ||||
|  | ||||
|   /// @brief Flush the output fifo. | ||||
|   /// @details If we refer to Serial.flush() in Arduino it says: ** Waits for the transmission of outgoing serial data | ||||
|   /// to complete. (Prior to Arduino 1.0, this the method was removing any buffered incoming serial data.). ** Therefore | ||||
|   /// we wait until all bytes are gone with a timeout of 100 ms | ||||
|   void flush() override; | ||||
|  | ||||
|  protected: | ||||
|   friend class WeikaiComponent; | ||||
|  | ||||
|   /// @brief this cannot happen with external uart therefore we do nothing | ||||
|   void check_logger_conflict() override {} | ||||
|  | ||||
|   /// @brief reset the weikai internal FIFO | ||||
|   void reset_fifo_(); | ||||
|  | ||||
|   /// @brief set the line parameters | ||||
|   void set_line_param_(); | ||||
|  | ||||
|   /// @brief set the baud rate | ||||
|   void set_baudrate_(); | ||||
|  | ||||
|   /// @brief Returns the number of bytes in the receive fifo | ||||
|   /// @return the number of bytes in the fifo | ||||
|   size_t rx_in_fifo_(); | ||||
|  | ||||
|   /// @brief Returns the number of bytes in the transmit fifo | ||||
|   /// @return the number of bytes in the fifo | ||||
|   size_t tx_in_fifo_(); | ||||
|  | ||||
|   /// @brief test if transmit buffer is not empty in the status register (optimization) | ||||
|   /// @return true if not emptygroup test_ | ||||
|   bool tx_fifo_is_not_empty_(); | ||||
|  | ||||
|   /// @brief transfer bytes from the weikai internal FIFO to the buffer (if any) | ||||
|   /// @return number of bytes transferred | ||||
|   size_t xfer_fifo_to_buffer_(); | ||||
|  | ||||
|   /// @brief check if channel is alive | ||||
|   /// @return true if OK | ||||
|   bool virtual check_channel_down(); | ||||
|  | ||||
| #ifdef TEST_COMPONENT | ||||
|   /// @ingroup test_ | ||||
|   /// @{ | ||||
|  | ||||
|   /// @brief Test the write_array() method | ||||
|   /// @param message to display | ||||
|   void uart_send_test_(char *message); | ||||
|  | ||||
|   /// @brief Test the read_array() method | ||||
|   /// @param message to display | ||||
|   /// @return true if success | ||||
|   bool uart_receive_test_(char *message); | ||||
|   /// @} | ||||
| #endif | ||||
|  | ||||
|   /// @brief the buffer where we store temporarily the bytes received | ||||
|   WKRingBuffer<uint8_t, RING_BUFFER_SIZE> receive_buffer_; | ||||
|   WeikaiComponent *parent_;  ///< our WK2168component parent | ||||
|   uint8_t channel_;          ///< our Channel number | ||||
|   uint8_t data_;             ///< a one byte buffer for register read storage | ||||
|   std::string name_;         ///< name of the entity | ||||
| }; | ||||
|  | ||||
| }  // namespace weikai | ||||
| }  // namespace esphome | ||||
							
								
								
									
										304
									
								
								esphome/components/weikai/wk_reg_def.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								esphome/components/weikai/wk_reg_def.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,304 @@ | ||||
| /// @file wk_reg_def.h | ||||
| /// @author DrCoolZic | ||||
| /// @brief  WeiKai component family - registers' definition | ||||
| /// @date Last Modified: 2024/02/18 15:49:18 | ||||
| #pragma once | ||||
|  | ||||
| namespace esphome { | ||||
| namespace weikai { | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////////////// | ||||
| /// Definition of the WeiKai registers | ||||
| //////////////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| /// @defgroup wk2168_gr_ WeiKai Global Registers | ||||
| /// This section groups all **Global Registers**: these registers are global to the | ||||
| /// the WeiKai chip (i.e. independent of the UART channel used) | ||||
| /// @note only registers and parameters used have been fully documented | ||||
| /// @{ | ||||
|  | ||||
| /// @brief Global Control Register - 00 0000 | ||||
| /// @details @code | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | bit | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   M0   |   M1   |       RSV       |  C4EN  |  C3EN  |  C2EN  |  C1EN  | name | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |    R   |    R   |    R   |    R   |   W/R  |   W/R  |   W/R  |   W/R  | type | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |    1   |    1   |    1   |    1   |    0   |    0   |    0   |    0   | reset | ||||
| ///  ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_GENA = 0x00; | ||||
| /// @brief Channel 4 enable clock (0: disable, 1: enable) | ||||
| constexpr uint8_t GENA_C4EN = 1 << 3; | ||||
| /// @brief Channel 3 enable clock (0: disable, 1: enable) | ||||
| constexpr uint8_t GENA_C3EN = 1 << 2; | ||||
| /// @brief Channel 2 enable clock (0: disable, 1: enable) | ||||
| constexpr uint8_t GENA_C2EN = 1 << 1; | ||||
| /// @brief Channel 1 enable clock (0: disable, 1: enable) | ||||
| constexpr uint8_t GENA_C1EN = 1 << 0; | ||||
|  | ||||
| /// @brief Global Reset Register - 00 0001 | ||||
| /// @details @code | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | bit | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  | C4SLEEP| C3SLEEP| C2SLEEP| C1SLEEP|  C4RST |  C3RST |  C2RST |  C1RST | name | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |    R   |    R   |    R   |    R   |  W1/R0 |  W1/R0 |  W1/R0 |  W1/R0 | type | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |    0   |    0   |    0   |    0   |    0   |    0   |    0   |    0   | reset | ||||
| ///  ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_GRST = 0x01; | ||||
| /// @brief Channel 4 soft reset (0: not reset, 1: reset) | ||||
| constexpr uint8_t GRST_C4RST = 1 << 3; | ||||
| /// @brief Channel 3 soft reset (0: not reset, 1: reset) | ||||
| constexpr uint8_t GRST_C3RST = 1 << 2; | ||||
| /// @brief Channel 2 soft reset (0: not reset, 1: reset) | ||||
| constexpr uint8_t GRST_C2RST = 1 << 1; | ||||
| /// @brief Channel 1 soft reset (0: not reset, 1: reset) | ||||
| constexpr uint8_t GRST_C1RST = 1 << 0; | ||||
|  | ||||
| /// @brief Global Master channel control register (not used) - 000010 | ||||
| constexpr uint8_t WKREG_GMUT = 0x02; | ||||
|  | ||||
| /// Global interrupt register (not used) - 01 0000 | ||||
| constexpr uint8_t WKREG_GIER = 0x10; | ||||
|  | ||||
| /// Global interrupt flag register (not used) 01 0001 | ||||
| constexpr uint8_t WKREG_GIFR = 0x11; | ||||
|  | ||||
| /// @brief Global GPIO direction register - 10 0001 | ||||
| /// @details @code | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | bit | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   PD7  |   PD6  |   PD5  |   PD4  |   PD3  |   PD2  |   PD1  |   PD0  | name | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  | type | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |    0   |    0   |    0   |    0   |    0   |    0   |    0   |    0   | reset | ||||
| ///  ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_GPDIR = 0x21; | ||||
|  | ||||
| /// @brief Global GPIO data register - 11 0001 | ||||
| /// @details @code | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | bit | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   PV7  |   PV6  |   PV5  |   PV4  |   PV3  |   PV2  |   PV1  |   PV0  | name | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  | type | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |    0   |    0   |    0   |    0   |    0   |    0   |    0   |    0   | reset | ||||
| ///  ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_GPDAT = 0x31; | ||||
|  | ||||
| /// @} | ||||
| /// @defgroup WeiKai_cr_ WeiKai Channel Registers | ||||
| /// @brief Definition of the register linked to a particular channel | ||||
| /// @details This topic groups all the **Channel Registers**: these registers are specific | ||||
| /// to the a specific channel i.e. each channel has its own set of registers | ||||
| /// @note only registers and parameters used have been documented | ||||
| /// @{ | ||||
|  | ||||
| /// @defgroup cr_p0 Channel registers when SPAGE=0 | ||||
| /// @brief Definition of the register linked to a particular channel when SPAGE=0 | ||||
| /// @details The channel registers are further splitted into two groups. | ||||
| /// This first group is defined when the Global register WKREG_SPAGE is 0 | ||||
| /// @{ | ||||
|  | ||||
| /// @brief Global Page register c0/c1 0011 | ||||
| /// @details @code | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | bit | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |                             RSV                              |  PAGE  | name | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |    R   |    R   |    R   |    R   |    R   |    R   |    R   |   W/R  | type | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |    0   |    0   |    0   |    0   |    0   |    0   |    0   |    0   | reset | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_SPAGE = 0x03; | ||||
|  | ||||
| /// @brief Serial Control Register - c0/c1 0100 | ||||
| /// @details @code | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | bit | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |                     RSV                    | SLPEN  |  TXEN  |  RXEN  | name | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |    R   |    R   |    R   |    R   |    R   |   R/W  |   R/W  |   W/R  | type | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |    0   |    0   |    0   |    0   |    0   |    0   |    0   |    0   | reset | ||||
| ///  ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_SCR = 0x04; | ||||
| /// @brief transmission control (0: enable, 1: disable) | ||||
| constexpr uint8_t SCR_TXEN = 1 << 1; | ||||
| /// @brief receiving control (0: enable, 1: disable) | ||||
| constexpr uint8_t SCR_RXEN = 1 << 0; | ||||
|  | ||||
| /// @brief Line Configuration Register - c0/c1 0101 | ||||
| /// @details @code | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | bit | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |        RSV      |  BREAK |  IREN  |  PAEN  |      PARITY     |  STPL  | name | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  | type | ||||
| ///  ------------------------------------------------------------------------- | ||||
| ///  |    0   |    0   |    0   |    0   |    0   |    0   |    0   |    0   | reset | ||||
| ///  ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_LCR = 0x05; | ||||
| /// @brief Parity enable (0: no check, 1: check) | ||||
| constexpr uint8_t LCR_PAEN = 1 << 3; | ||||
| /// @brief Parity force 0 | ||||
| constexpr uint8_t LCR_PAR_F0 = 0 << 1; | ||||
| /// @brief Parity odd | ||||
| constexpr uint8_t LCR_PAR_ODD = 1 << 1; | ||||
| /// @brief Parity even | ||||
| constexpr uint8_t LCR_PAR_EVEN = 2 << 1; | ||||
| /// @brief Parity force 1 | ||||
| constexpr uint8_t LCR_PAR_F1 = 3 << 1; | ||||
| /// @brief Stop length (0: 1 bit, 1: 2 bits) | ||||
| constexpr uint8_t LCR_STPL = 1 << 0; | ||||
|  | ||||
| /// @brief FIFO Control Register - c0/c1 0110 | ||||
| /// @details @code | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | bit | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |      TFTRIG     |      RFTRIG     |  TFEN  |  RFEN  |  TFRST |  RFRST | name | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  | type | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |    0   |    0   |    0   |    0   |    0   |    0   |    0   |    0   | reset | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_FCR = 0x06; | ||||
| /// @brief Transmitter FIFO enable | ||||
| constexpr uint8_t FCR_TFEN = 1 << 3; | ||||
| /// @brief Receiver FIFO enable | ||||
| constexpr uint8_t FCR_RFEN = 1 << 2; | ||||
| /// @brief Transmitter FIFO reset | ||||
| constexpr uint8_t FCR_TFRST = 1 << 1; | ||||
| /// @brief Receiver FIFO reset | ||||
| constexpr uint8_t FCR_RFRST = 1 << 0; | ||||
|  | ||||
| /// @brief Serial Interrupt Enable Register (not used)  - c0/c1 0111 | ||||
| constexpr uint8_t WKREG_SIER = 0x07; | ||||
|  | ||||
| /// @brief Serial Interrupt Flag Register (not used) - c0/c1 1000 | ||||
| constexpr uint8_t WKREG_SIFR = 0x08; | ||||
|  | ||||
| /// @brief Transmitter FIFO Count - c0/c1 1001 | ||||
| /// @details @code | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |                  NUMBER OF DATA IN TRANSMITTER FIFO                   | | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_TFCNT = 0x09; | ||||
|  | ||||
| /// @brief Receiver FIFO count - c0/c1 1010 | ||||
| /// @details @code | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |                    NUMBER OF DATA IN RECEIVER FIFO                    | | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_RFCNT = 0x0A; | ||||
|  | ||||
| /// @brief FIFO Status Register - c0/c1 1011 | ||||
| /// @details @code | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | bit | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |  RFOE  |  RFLB  |  RFFE  |  RFPE  | RFDAT  | TFDAT  | TFFULL |  TBUSY | name | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |    R   |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  |   W/R  | type | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |    0   |    0   |    0   |    0   |    0   |    0   |    0   |    0   | reset | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| /// @warning The received buffer can hold 256 bytes. However, as the RFCNT reg | ||||
| /// is 8 bits, if we have 256 byte in the register this is reported as 0 ! Therefore | ||||
| /// RFCNT=0 can indicate that there are 0 **or** 256 bytes in the buffer. If we | ||||
| /// have RFDAT = 1 and RFCNT = 0 it should be interpreted as 256 bytes in the FIFO. | ||||
| /// @note Note that in case of overflow the RFOE goes to one **but** as soon as you read | ||||
| /// the FSR this bit is cleared. Therefore Overflow can be read only once. | ||||
| /// @n The same problem applies to the transmit buffer but here we have to check the | ||||
| /// TFFULL flag. So if TFFULL is set and TFCNT is 0 this should be interpreted as 256 | ||||
| constexpr uint8_t WKREG_FSR = 0x0B; | ||||
| /// @brief Receiver FIFO Overflow Error (0: no OE, 1: OE) | ||||
| constexpr uint8_t FSR_RFOE = 1 << 7; | ||||
| /// @brief Receiver FIFO Line Break (0: no LB, 1: LB) | ||||
| constexpr uint8_t FSR_RFLB = 1 << 6; | ||||
| /// @brief Receiver FIFO Frame Error (0: no FE, 1: FE) | ||||
| constexpr uint8_t FSR_RFFE = 1 << 5; | ||||
| /// @brief Receiver Parity Error (0: no PE, 1: PE) | ||||
| constexpr uint8_t FSR_RFPE = 1 << 4; | ||||
| /// @brief Receiver FIFO count (0: empty, 1: not empty) | ||||
| constexpr uint8_t FSR_RFDAT = 1 << 3; | ||||
| /// @brief Transmitter FIFO count (0: empty, 1: not empty) | ||||
| constexpr uint8_t FSR_TFDAT = 1 << 2; | ||||
| /// @brief Transmitter FIFO full (0: not full, 1: full) | ||||
| constexpr uint8_t FSR_TFFULL = 1 << 1; | ||||
| /// @brief Transmitter busy (0 nothing to transmit, 1: transmitter busy sending) | ||||
| constexpr uint8_t FSR_TBUSY = 1 << 0; | ||||
|  | ||||
| /// @brief Line Status Register (not used - using FIFO) | ||||
| constexpr uint8_t WKREG_LSR = 0x0C; | ||||
|  | ||||
| /// @brief FIFO Data Register (not used - does not seems to work) | ||||
| constexpr uint8_t WKREG_FDAT = 0x0D; | ||||
|  | ||||
| /// @} | ||||
| /// @defgroup cr_p1 Channel registers for SPAGE=1 | ||||
| /// @brief Definition of the register linked to a particular channel when SPAGE=1 | ||||
| /// @details The channel registers are further splitted into two groups. | ||||
| /// This second group is defined when the Global register WKREG_SPAGE is 1 | ||||
| /// @{ | ||||
|  | ||||
| /// @brief Baud rate configuration register: high byte - c0/c1 0100 | ||||
| /// @details @code | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |                      High byte of the baud rate                       | | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_BRH = 0x04; | ||||
|  | ||||
| /// @brief Baud rate configuration register: low byte - c0/c1 0101 | ||||
| /// @details @code | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |   b7   |   b6   |   b5   |   b4   |   b3   |   b2   |   b1   |   b0   | | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// |                       Low byte of the baud rate                       | | ||||
| /// ------------------------------------------------------------------------- | ||||
| /// @endcode | ||||
| constexpr uint8_t WKREG_BRL = 0x05; | ||||
|  | ||||
| /// @brief Baud rate configuration register decimal part - c0/c1 0110 | ||||
| constexpr uint8_t WKREG_BRD = 0x06; | ||||
|  | ||||
| /// @brief Receive FIFO Interrupt trigger configuration (not used) - c0/c1 0111 | ||||
| constexpr uint8_t WKREG_RFI = 0x07; | ||||
|  | ||||
| /// @brief Transmit FIFO interrupt trigger configuration (not used) - c0/c1 1000 | ||||
| constexpr uint8_t WKREG_TFI = 0x08; | ||||
|  | ||||
| /// @} | ||||
| /// @} | ||||
| }  // namespace weikai | ||||
| }  // namespace esphome | ||||
							
								
								
									
										1
									
								
								esphome/components/weikai_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/weikai_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
							
								
								
									
										177
									
								
								esphome/components/weikai_i2c/weikai_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								esphome/components/weikai_i2c/weikai_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | ||||
| /// @file weikai_i2c.cpp | ||||
| /// @brief  WeiKai component family - classes implementation | ||||
| /// @date Last Modified: 2024/04/06 14:43:31 | ||||
| /// @details The classes declared in this file can be used by the Weikai family | ||||
|  | ||||
| #include "weikai_i2c.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace weikai_i2c { | ||||
| static const char *const TAG = "weikai_i2c"; | ||||
|  | ||||
| /// @brief Display a buffer in hexadecimal format (32 hex values / line). | ||||
| void print_buffer(const uint8_t *data, size_t length) { | ||||
|   char hex_buffer[100]; | ||||
|   hex_buffer[(3 * 32) + 1] = 0; | ||||
|   for (size_t i = 0; i < length; i++) { | ||||
|     snprintf(&hex_buffer[3 * (i % 32)], sizeof(hex_buffer), "%02X ", data[i]); | ||||
|     if (i % 32 == 31) { | ||||
|       ESP_LOGVV(TAG, "   %s", hex_buffer); | ||||
|     } | ||||
|   } | ||||
|   if (length % 32) { | ||||
|     // null terminate if incomplete line | ||||
|     hex_buffer[3 * (length % 32) + 2] = 0; | ||||
|     ESP_LOGVV(TAG, "   %s", hex_buffer); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static const char *const REG_TO_STR_P0[16] = {"GENA", "GRST",  "GMUT",  "SPAGE", "SCR", "LCR",  "FCR",  "SIER", | ||||
|                                               "SIFR", "TFCNT", "RFCNT", "FSR",   "LSR", "FDAT", "FWCR", "RS485"}; | ||||
| static const char *const REG_TO_STR_P1[16] = {"GENA", "GRST", "GMUT", "SPAGE", "BAUD1", "BAUD0", "PRES", "RFTL", | ||||
|                                               "TFTL", "FWTH", "FWTL", "XON1",  "XOFF1", "SADR",  "SAEN", "RTSDLY"}; | ||||
| using namespace weikai; | ||||
| // method to print a register value as text: used in the log messages ... | ||||
| const char *reg_to_str(int reg, bool page1) { | ||||
|   if (reg == WKREG_GPDAT) { | ||||
|     return "GPDAT"; | ||||
|   } else if (reg == WKREG_GPDIR) { | ||||
|     return "GPDIR"; | ||||
|   } else { | ||||
|     return page1 ? REG_TO_STR_P1[reg & 0x0F] : REG_TO_STR_P0[reg & 0x0F]; | ||||
|   } | ||||
| } | ||||
| enum RegType { REG = 0, FIFO = 1 };  ///< Register or FIFO | ||||
|  | ||||
| /// @brief Computes the I²C bus's address used to access the component | ||||
| /// @param base_address the base address of the component - set by the A1 A0 pins | ||||
| /// @param channel (0-3) the UART channel | ||||
| /// @param fifo (0-1) 0 = access to internal register, 1 = direct access to fifo | ||||
| /// @return the i2c address to use | ||||
| inline uint8_t i2c_address(uint8_t base_address, uint8_t channel, RegType fifo) { | ||||
|   // the address of the device is: | ||||
|   // +----+----+----+----+----+----+----+----+ | ||||
|   // |  0 | A1 | A0 |  1 |  0 | C1 | C0 |  F | | ||||
|   // +----+----+----+----+----+----+----+----+ | ||||
|   // where: | ||||
|   // - A1,A0 is the address read from A1,A0 switch | ||||
|   // - C1,C0 is the channel number (in practice only 00 or 01) | ||||
|   // - F is: 0 when accessing register, one when accessing FIFO | ||||
|   uint8_t const addr = base_address | channel << 1 | fifo << 0; | ||||
|   return addr; | ||||
| } | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| // The WeikaiRegisterI2C methods | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| uint8_t WeikaiRegisterI2C::read_reg() const { | ||||
|   uint8_t value = 0x00; | ||||
|   WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_); | ||||
|   uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, REG); | ||||
|   comp_i2c->set_i2c_address(address); | ||||
|   auto error = comp_i2c->read_register(this->register_, &value, 1); | ||||
|   if (error == i2c::NO_ERROR) { | ||||
|     this->comp_->status_clear_warning(); | ||||
|     ESP_LOGVV(TAG, "WeikaiRegisterI2C::read_reg() @%02X reg=%s ch=%u I2C_code:%d, buf=%02X", address, | ||||
|               reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value); | ||||
|   } else {  // error | ||||
|     this->comp_->status_set_warning(); | ||||
|     ESP_LOGE(TAG, "WeikaiRegisterI2C::read_reg() @%02X reg=%s ch=%u I2C_code:%d, buf=%02X", address, | ||||
|              reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value); | ||||
|   } | ||||
|   return value; | ||||
| } | ||||
|  | ||||
| void WeikaiRegisterI2C::read_fifo(uint8_t *data, size_t length) const { | ||||
|   WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_); | ||||
|   uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, FIFO); | ||||
|   comp_i2c->set_i2c_address(address); | ||||
|   auto error = comp_i2c->read(data, length); | ||||
|   if (error == i2c::NO_ERROR) { | ||||
|     this->comp_->status_clear_warning(); | ||||
| #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE | ||||
|     ESP_LOGVV(TAG, "WeikaiRegisterI2C::read_fifo() @%02X ch=%d I2C_code:%d len=%d buffer", address, this->channel_, | ||||
|               (int) error, length); | ||||
|     print_buffer(data, length); | ||||
| #endif | ||||
|   } else {  // error | ||||
|     this->comp_->status_set_warning(); | ||||
|     ESP_LOGE(TAG, "WeikaiRegisterI2C::read_fifo() @%02X reg=N/A ch=%d I2C_code:%d len=%d buf=%02X...", address, | ||||
|              this->channel_, (int) error, length, data[0]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void WeikaiRegisterI2C::write_reg(uint8_t value) { | ||||
|   WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_); | ||||
|   uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, REG);  // update the i2c bus | ||||
|   comp_i2c->set_i2c_address(address); | ||||
|   auto error = comp_i2c->write_register(this->register_, &value, 1); | ||||
|   if (error == i2c::NO_ERROR) { | ||||
|     this->comp_->status_clear_warning(); | ||||
|     ESP_LOGVV(TAG, "WK2168Reg::write_reg() @%02X reg=%s ch=%d I2C_code:%d buf=%02X", address, | ||||
|               reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value); | ||||
|   } else {  // error | ||||
|     this->comp_->status_set_warning(); | ||||
|     ESP_LOGE(TAG, "WK2168Reg::write_reg() @%02X reg=%s ch=%d I2C_code:%d buf=%d", address, | ||||
|              reg_to_str(this->register_, comp_i2c->page1()), this->channel_, (int) error, value); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void WeikaiRegisterI2C::write_fifo(uint8_t *data, size_t length) { | ||||
|   WeikaiComponentI2C *comp_i2c = static_cast<WeikaiComponentI2C *>(this->comp_); | ||||
|   uint8_t address = i2c_address(comp_i2c->base_address_, this->channel_, FIFO);  // set fifo flag | ||||
|   comp_i2c->set_i2c_address(address); | ||||
|   auto error = comp_i2c->write(data, length); | ||||
|   if (error == i2c::NO_ERROR) { | ||||
|     this->comp_->status_clear_warning(); | ||||
| #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE | ||||
|     ESP_LOGVV(TAG, "WK2168Reg::write_fifo() @%02X ch=%d I2C_code:%d len=%d buffer", address, this->channel_, | ||||
|               (int) error, length); | ||||
|     print_buffer(data, length); | ||||
| #endif | ||||
|   } else {  // error | ||||
|     this->comp_->status_set_warning(); | ||||
|     ESP_LOGE(TAG, "WK2168Reg::write_fifo() @%02X reg=N/A, ch=%d I2C_code:%d len=%d, buf=%02X...", address, | ||||
|              this->channel_, (int) error, length, data[0]); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| // The WeikaiComponentI2C methods | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| void WeikaiComponentI2C::setup() { | ||||
|   // before any manipulation we store the address to base_address_ for future use | ||||
|   this->base_address_ = this->address_; | ||||
|   ESP_LOGCONFIG(TAG, "Setting up wk2168_i2c: %s with %d UARTs at @%02X ...", this->get_name(), this->children_.size(), | ||||
|                 this->base_address_); | ||||
|  | ||||
|   // enable all channels | ||||
|   this->reg(WKREG_GENA, 0) = GENA_C1EN | GENA_C2EN | GENA_C3EN | GENA_C4EN; | ||||
|   // reset all channels | ||||
|   this->reg(WKREG_GRST, 0) = GRST_C1RST | GRST_C2RST | GRST_C3RST | GRST_C4RST; | ||||
|   // initialize the spage register to page 0 | ||||
|   this->reg(WKREG_SPAGE, 0) = 0; | ||||
|   this->page1_ = false; | ||||
|  | ||||
|   // we setup our children channels | ||||
|   for (auto *child : this->children_) { | ||||
|     child->setup_channel(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void WeikaiComponentI2C::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "Initialization of %s with %d UARTs completed", this->get_name(), this->children_.size()); | ||||
|   ESP_LOGCONFIG(TAG, "  Crystal: %" PRIu32, this->crystal_); | ||||
|   if (test_mode_) | ||||
|     ESP_LOGCONFIG(TAG, "  Test mode: %d", test_mode_); | ||||
|   ESP_LOGCONFIG(TAG, "  Transfer buffer size: %d", XFER_MAX_SIZE); | ||||
|   this->address_ = this->base_address_;  // we restore the base_address before display (less confusing) | ||||
|   LOG_I2C_DEVICE(this); | ||||
|  | ||||
|   for (auto *child : this->children_) { | ||||
|     child->dump_channel(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace weikai_i2c | ||||
| }  // namespace esphome | ||||
							
								
								
									
										61
									
								
								esphome/components/weikai_i2c/weikai_i2c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								esphome/components/weikai_i2c/weikai_i2c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| /// @file weikai_i2c.h | ||||
| /// @author DrCoolZic | ||||
| /// @brief  WeiKai component family - classes declaration | ||||
| /// @date Last Modified: 2024/03/01 13:31:57 | ||||
| /// @details The classes declared in this file can be used by the Weikai family | ||||
| ///          wk2132_i2c, wk2168_i2c, wk2204_i2c, wk2212_i2c | ||||
|  | ||||
| #pragma once | ||||
| #include <bitset> | ||||
| #include <memory> | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
| #include "esphome/components/i2c/i2c.h" | ||||
| #include "esphome/components/weikai/weikai.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace weikai_i2c { | ||||
|  | ||||
| class WeikaiComponentI2C; | ||||
|  | ||||
| // using namespace weikai; | ||||
| //////////////////////////////////////////////////////////////////////////////////// | ||||
| /// @brief WeikaiRegisterI2C objects acts as proxies to access remote register through an I2C Bus | ||||
| //////////////////////////////////////////////////////////////////////////////////// | ||||
| class WeikaiRegisterI2C : public weikai::WeikaiRegister { | ||||
|  public: | ||||
|   uint8_t read_reg() const override; | ||||
|   void write_reg(uint8_t value) override; | ||||
|   void read_fifo(uint8_t *data, size_t length) const override; | ||||
|   void write_fifo(uint8_t *data, size_t length) override; | ||||
|  | ||||
|  protected: | ||||
|   friend WeikaiComponentI2C; | ||||
|   WeikaiRegisterI2C(weikai::WeikaiComponent *const comp, uint8_t reg, uint8_t channel) | ||||
|       : weikai::WeikaiRegister(comp, reg, channel) {} | ||||
| }; | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////////// | ||||
| /// @brief The WeikaiComponentI2C class stores the information to the WeiKai component | ||||
| /// connected through an I2C bus. | ||||
| //////////////////////////////////////////////////////////////////////////////////// | ||||
| class WeikaiComponentI2C : public weikai::WeikaiComponent, public i2c::I2CDevice { | ||||
|  public: | ||||
|   weikai::WeikaiRegister ®(uint8_t reg, uint8_t channel) override { | ||||
|     reg_i2c_.register_ = reg; | ||||
|     reg_i2c_.channel_ = channel; | ||||
|     return reg_i2c_; | ||||
|   } | ||||
|  | ||||
|   // | ||||
|   // override Component methods | ||||
|   // | ||||
|   void setup() override; | ||||
|   void dump_config() override; | ||||
|  | ||||
|   uint8_t base_address_;                   ///< base address of I2C device | ||||
|   WeikaiRegisterI2C reg_i2c_{this, 0, 0};  ///< init to this component | ||||
| }; | ||||
|  | ||||
| }  // namespace weikai_i2c | ||||
| }  // namespace esphome | ||||
							
								
								
									
										1
									
								
								esphome/components/weikai_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/weikai_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
							
								
								
									
										189
									
								
								esphome/components/weikai_spi/weikai_spi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								esphome/components/weikai_spi/weikai_spi.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | ||||
| /// @file weikai_spi.cpp | ||||
| /// @brief  WeiKai component family - classes implementation | ||||
| /// @date Last Modified: 2024/04/06 14:46:09 | ||||
| /// @details The classes declared in this file can be used by the Weikai family | ||||
|  | ||||
| #include "weikai_spi.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace weikai_spi { | ||||
| using namespace weikai; | ||||
| static const char *const TAG = "weikai_spi"; | ||||
|  | ||||
| /// @brief convert an int to binary representation as C++ std::string | ||||
| /// @param val integer to convert | ||||
| /// @return a std::string | ||||
| inline std::string i2s(uint8_t val) { return std::bitset<8>(val).to_string(); } | ||||
| /// Convert std::string to C string | ||||
| #define I2S2CS(val) (i2s(val).c_str()) | ||||
|  | ||||
| /// @brief measure the time elapsed between two calls | ||||
| /// @param last_time time of the previous call | ||||
| /// @return the elapsed time in microseconds | ||||
| uint32_t elapsed_ms(uint32_t &last_time) { | ||||
|   uint32_t e = millis() - last_time; | ||||
|   last_time = millis(); | ||||
|   return e; | ||||
| }; | ||||
|  | ||||
| /// @brief Converts the parity enum value to a C string | ||||
| /// @param parity enum | ||||
| /// @return the string | ||||
| const char *p2s(uart::UARTParityOptions parity) { | ||||
|   using namespace uart; | ||||
|   switch (parity) { | ||||
|     case UART_CONFIG_PARITY_NONE: | ||||
|       return "NONE"; | ||||
|     case UART_CONFIG_PARITY_EVEN: | ||||
|       return "EVEN"; | ||||
|     case UART_CONFIG_PARITY_ODD: | ||||
|       return "ODD"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// @brief Display a buffer in hexadecimal format (32 hex values / line). | ||||
| void print_buffer(const uint8_t *data, size_t length) { | ||||
|   char hex_buffer[100]; | ||||
|   hex_buffer[(3 * 32) + 1] = 0; | ||||
|   for (size_t i = 0; i < length; i++) { | ||||
|     snprintf(&hex_buffer[3 * (i % 32)], sizeof(hex_buffer), "%02X ", data[i]); | ||||
|     if (i % 32 == 31) { | ||||
|       ESP_LOGVV(TAG, "   %s", hex_buffer); | ||||
|     } | ||||
|   } | ||||
|   if (length % 32) { | ||||
|     // null terminate if incomplete line | ||||
|     hex_buffer[3 * (length % 32) + 2] = 0; | ||||
|     ESP_LOGVV(TAG, "   %s", hex_buffer); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static const char *const REG_TO_STR_P0[16] = {"GENA", "GRST",  "GMUT",  "SPAGE", "SCR", "LCR",  "FCR",  "SIER", | ||||
|                                               "SIFR", "TFCNT", "RFCNT", "FSR",   "LSR", "FDAT", "FWCR", "RS485"}; | ||||
| static const char *const REG_TO_STR_P1[16] = {"GENA", "GRST", "GMUT", "SPAGE", "BAUD1", "BAUD0", "PRES", "RFTL", | ||||
|                                               "TFTL", "FWTH", "FWTL", "XON1",  "XOFF1", "SADR",  "SAEN", "RTSDLY"}; | ||||
|  | ||||
| // method to print a register value as text: used in the log messages ... | ||||
| const char *reg_to_str(int reg, bool page1) { | ||||
|   if (reg == WKREG_GPDAT) { | ||||
|     return "GPDAT"; | ||||
|   } else if (reg == WKREG_GPDIR) { | ||||
|     return "GPDIR"; | ||||
|   } else { | ||||
|     return page1 ? REG_TO_STR_P1[reg & 0x0F] : REG_TO_STR_P0[reg & 0x0F]; | ||||
|   } | ||||
| } | ||||
|  | ||||
| enum RegType { REG = 0, FIFO = 1 };            ///< Register or FIFO | ||||
| enum CmdType { WRITE_CMD = 0, READ_CMD = 1 };  ///< Read or Write transfer | ||||
|  | ||||
| /// @brief Computes the SPI command byte | ||||
| /// @param transfer_type read or write command | ||||
| /// @param reg (0-15) the address of the register | ||||
| /// @param channel (0-3) the UART channel | ||||
| /// @param fifo (0-1) 0 = access to internal register, 1 = direct access to fifo | ||||
| /// @return the spi command byte | ||||
| /// @details | ||||
| /// +------+------+------+------+------+------+------+------+ | ||||
| /// | FIFO | R/W  |    C1-C0    |           A3-A0           | | ||||
| /// +------+------+-------------+---------------------------+ | ||||
| /// FIFO: 0 = register, 1 = FIFO | ||||
| /// R/W: 0 = write, 1 = read | ||||
| /// C1-C0: Channel (0-1) | ||||
| /// A3-A0: Address (0-F) | ||||
| inline static uint8_t cmd_byte(RegType fifo, CmdType transfer_type, uint8_t channel, uint8_t reg) { | ||||
|   return (fifo << 7 | transfer_type << 6 | channel << 4 | reg << 0); | ||||
| } | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| // The WeikaiRegisterSPI methods | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| uint8_t WeikaiRegisterSPI::read_reg() const { | ||||
|   auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_); | ||||
|   uint8_t cmd = cmd_byte(REG, READ_CMD, this->channel_, this->register_); | ||||
|   spi_comp->enable(); | ||||
|   spi_comp->write_byte(cmd); | ||||
|   uint8_t val = spi_comp->read_byte(); | ||||
|   spi_comp->disable(); | ||||
|   ESP_LOGVV(TAG, "WeikaiRegisterSPI::read_reg() cmd=%s(%02X) reg=%s ch=%d buf=%02X", I2S2CS(cmd), cmd, | ||||
|             reg_to_str(this->register_, this->comp_->page1()), this->channel_, val); | ||||
|   return val; | ||||
| } | ||||
|  | ||||
| void WeikaiRegisterSPI::read_fifo(uint8_t *data, size_t length) const { | ||||
|   auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_); | ||||
|   uint8_t cmd = cmd_byte(FIFO, READ_CMD, this->channel_, this->register_); | ||||
|   spi_comp->enable(); | ||||
|   spi_comp->write_byte(cmd); | ||||
|   spi_comp->read_array(data, length); | ||||
|   spi_comp->disable(); | ||||
| #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE | ||||
|   ESP_LOGVV(TAG, "WeikaiRegisterSPI::read_fifo() cmd=%s(%02X) ch=%d len=%d buffer", I2S2CS(cmd), cmd, this->channel_, | ||||
|             length); | ||||
|   print_buffer(data, length); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void WeikaiRegisterSPI::write_reg(uint8_t value) { | ||||
|   auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_); | ||||
|   uint8_t buf[2]{cmd_byte(REG, WRITE_CMD, this->channel_, this->register_), value}; | ||||
|   spi_comp->enable(); | ||||
|   spi_comp->write_array(buf, 2); | ||||
|   spi_comp->disable(); | ||||
|   ESP_LOGVV(TAG, "WeikaiRegisterSPI::write_reg() cmd=%s(%02X) reg=%s ch=%d buf=%02X", I2S2CS(buf[0]), buf[0], | ||||
|             reg_to_str(this->register_, this->comp_->page1()), this->channel_, buf[1]); | ||||
| } | ||||
|  | ||||
| void WeikaiRegisterSPI::write_fifo(uint8_t *data, size_t length) { | ||||
|   auto *spi_comp = static_cast<WeikaiComponentSPI *>(this->comp_); | ||||
|   uint8_t cmd = cmd_byte(FIFO, WRITE_CMD, this->channel_, this->register_); | ||||
|   spi_comp->enable(); | ||||
|   spi_comp->write_byte(cmd); | ||||
|   spi_comp->write_array(data, length); | ||||
|   spi_comp->disable(); | ||||
|  | ||||
| #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE | ||||
|   ESP_LOGVV(TAG, "WeikaiRegisterSPI::write_fifo() cmd=%s(%02X) ch=%d len=%d buffer", I2S2CS(cmd), cmd, this->channel_, | ||||
|             length); | ||||
|   print_buffer(data, length); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| // The WeikaiComponentSPI methods | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| void WeikaiComponentSPI::setup() { | ||||
|   using namespace weikai; | ||||
|   ESP_LOGCONFIG(TAG, "Setting up wk2168_spi: %s with %d UARTs...", this->get_name(), this->children_.size()); | ||||
|   this->spi_setup(); | ||||
|   // enable all channels | ||||
|   this->reg(WKREG_GENA, 0) = GENA_C1EN | GENA_C2EN | GENA_C3EN | GENA_C4EN; | ||||
|   // reset all channels | ||||
|   this->reg(WKREG_GRST, 0) = GRST_C1RST | GRST_C2RST | GRST_C3RST | GRST_C4RST; | ||||
|   // initialize the spage register to page 0 | ||||
|   this->reg(WKREG_SPAGE, 0) = 0; | ||||
|   this->page1_ = false; | ||||
|  | ||||
|   // we setup our children channels | ||||
|   for (auto *child : this->children_) { | ||||
|     child->setup_channel(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void WeikaiComponentSPI::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "Initialization of %s with %d UARTs completed", this->get_name(), this->children_.size()); | ||||
|   ESP_LOGCONFIG(TAG, "  Crystal: %" PRIu32 "", this->crystal_); | ||||
|   if (test_mode_) | ||||
|     ESP_LOGCONFIG(TAG, "  Test mode: %d", test_mode_); | ||||
|   ESP_LOGCONFIG(TAG, "  Transfer buffer size: %d", XFER_MAX_SIZE); | ||||
|   LOG_PIN("  CS Pin: ", this->cs_); | ||||
|  | ||||
|   for (auto *child : this->children_) { | ||||
|     child->dump_channel(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace weikai_spi | ||||
| }  // namespace esphome | ||||
							
								
								
									
										54
									
								
								esphome/components/weikai_spi/weikai_spi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								esphome/components/weikai_spi/weikai_spi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| /// @file weikai.h | ||||
| /// @author DrCoolZic | ||||
| /// @brief  WeiKai component family - classes declaration | ||||
| /// @date Last Modified: 2024/02/29 17:20:32 | ||||
| /// @details The classes declared in this file can be used by the Weikai family | ||||
| ///          wk2124_spi, wk2132_spi, wk2168_spi, wk2204_spi, wk2212_spi, | ||||
|  | ||||
| #pragma once | ||||
| #include <bitset> | ||||
| #include <memory> | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
| #include "esphome/components/spi/spi.h" | ||||
| #include "esphome/components/weikai/weikai.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace weikai_spi { | ||||
| //////////////////////////////////////////////////////////////////////////////////// | ||||
| /// @brief WeikaiRegisterSPI objects acts as proxies to access remote register through an SPI Bus | ||||
| //////////////////////////////////////////////////////////////////////////////////// | ||||
| class WeikaiRegisterSPI : public weikai::WeikaiRegister { | ||||
|  public: | ||||
|   WeikaiRegisterSPI(weikai::WeikaiComponent *const comp, uint8_t reg, uint8_t channel) | ||||
|       : weikai::WeikaiRegister(comp, reg, channel) {} | ||||
|  | ||||
|   uint8_t read_reg() const override; | ||||
|   void write_reg(uint8_t value) override; | ||||
|   void read_fifo(uint8_t *data, size_t length) const override; | ||||
|   void write_fifo(uint8_t *data, size_t length) override; | ||||
| }; | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////////// | ||||
| /// @brief The WeikaiComponentSPI class stores the information to the WeiKai component | ||||
| /// connected through an SPI bus. | ||||
| //////////////////////////////////////////////////////////////////////////////////// | ||||
| class WeikaiComponentSPI : public weikai::WeikaiComponent, | ||||
|                            public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, | ||||
|                                                  spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> { | ||||
|  public: | ||||
|   weikai::WeikaiRegister ®(uint8_t reg, uint8_t channel) override { | ||||
|     reg_spi_.register_ = reg; | ||||
|     reg_spi_.channel_ = channel; | ||||
|     return reg_spi_; | ||||
|   } | ||||
|  | ||||
|   void setup() override; | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   WeikaiRegisterSPI reg_spi_{this, 0, 0};  ///< init to this component | ||||
| }; | ||||
|  | ||||
| }  // namespace weikai_spi | ||||
| }  // namespace esphome | ||||
| @@ -128,7 +128,7 @@ void WiFiComponent::loop() { | ||||
|       case WIFI_COMPONENT_STATE_COOLDOWN: { | ||||
|         this->status_set_warning(); | ||||
|         if (millis() - this->action_started_ > 5000) { | ||||
|           if (this->fast_connect_) { | ||||
|           if (this->fast_connect_ || this->retry_hidden_) { | ||||
|             this->start_connecting(this->sta_[0], false); | ||||
|           } else { | ||||
|             this->start_scanning(); | ||||
| @@ -591,6 +591,9 @@ void WiFiComponent::check_connecting_finished() { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // We won't retry hidden networks unless a reconnect fails more than three times again | ||||
|     this->retry_hidden_ = false; | ||||
|  | ||||
|     ESP_LOGI(TAG, "WiFi Connected!"); | ||||
|     this->print_connect_params_(); | ||||
|  | ||||
| @@ -668,10 +671,11 @@ void WiFiComponent::retry_connect() { | ||||
|       this->wifi_mode_(false, {}); | ||||
|       delay(100);  // NOLINT | ||||
|       this->num_retried_ = 0; | ||||
|       this->retry_hidden_ = false; | ||||
|     } else { | ||||
|       // Try hidden networks after 3 failed retries | ||||
|       ESP_LOGD(TAG, "Retrying with hidden networks..."); | ||||
|       this->fast_connect_ = true; | ||||
|       this->retry_hidden_ = true; | ||||
|       this->num_retried_++; | ||||
|     } | ||||
|   } else { | ||||
|   | ||||
| @@ -371,6 +371,7 @@ class WiFiComponent : public Component { | ||||
|   std::vector<WiFiSTAPriority> sta_priorities_; | ||||
|   WiFiAP selected_ap_; | ||||
|   bool fast_connect_{false}; | ||||
|   bool retry_hidden_{false}; | ||||
|  | ||||
|   bool has_ap_{false}; | ||||
|   WiFiAP ap_; | ||||
|   | ||||
							
								
								
									
										30
									
								
								esphome/components/wk2132_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								esphome/components/wk2132_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c, weikai | ||||
| from esphome.const import CONF_ID | ||||
|  | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
| DEPENDENCIES = ["i2c"] | ||||
| AUTO_LOAD = ["weikai", "weikai_i2c"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c") | ||||
| WeikaiComponentI2C = weikai_i2c_ns.class_( | ||||
|     "WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     weikai.WKBASE_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(WeikaiComponentI2C), | ||||
|         } | ||||
|     ).extend(i2c.i2c_device_schema(0x2C)), | ||||
|     weikai.check_channel_max_2, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     cg.add(var.set_name(str(config[CONF_ID]))) | ||||
|     await weikai.register_weikai(var, config) | ||||
|     await i2c.register_i2c_device(var, config) | ||||
							
								
								
									
										4
									
								
								esphome/components/wk2132_i2c/wk2132_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								esphome/components/wk2132_i2c/wk2132_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| /* compiling with esp-idf framework requires a .cpp file for some reason ? */ | ||||
| namespace esphome { | ||||
| namespace wk2132_i2c {} | ||||
| }  // namespace esphome | ||||
							
								
								
									
										31
									
								
								esphome/components/wk2132_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								esphome/components/wk2132_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import spi, weikai | ||||
|  | ||||
| from esphome.const import CONF_ID | ||||
|  | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
| DEPENDENCIES = ["spi"] | ||||
| AUTO_LOAD = ["weikai", "weikai_spi"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi") | ||||
| WeikaiComponentSPI = weikai_spi_ns.class_( | ||||
|     "WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     weikai.WKBASE_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(WeikaiComponentSPI), | ||||
|         } | ||||
|     ).extend(spi.spi_device_schema()), | ||||
|     weikai.check_channel_max_2, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     cg.add(var.set_name(str(config[CONF_ID]))) | ||||
|     await weikai.register_weikai(var, config) | ||||
|     await spi.register_spi_device(var, config) | ||||
							
								
								
									
										64
									
								
								esphome/components/wk2168_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								esphome/components/wk2168_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import i2c, weikai | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_INVERTED, | ||||
|     CONF_MODE, | ||||
|     CONF_NUMBER, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
| DEPENDENCIES = ["i2c"] | ||||
| AUTO_LOAD = ["weikai", "weikai_i2c"] | ||||
| MULTI_CONF = True | ||||
| CONF_WK2168_I2C = "wk2168_i2c" | ||||
|  | ||||
| weikai_ns = cg.esphome_ns.namespace("weikai") | ||||
| weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c") | ||||
| WeikaiComponentI2C = weikai_i2c_ns.class_( | ||||
|     "WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice | ||||
| ) | ||||
| WeikaiGPIOPin = weikai_ns.class_( | ||||
|     "WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentI2C) | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     weikai.WKBASE_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(WeikaiComponentI2C), | ||||
|         } | ||||
|     ).extend(i2c.i2c_device_schema(0x2C)), | ||||
|     weikai.check_channel_max_4, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     cg.add(var.set_name(str(config[CONF_ID]))) | ||||
|     await weikai.register_weikai(var, config) | ||||
|     await i2c.register_i2c_device(var, config) | ||||
|  | ||||
|  | ||||
| WK2168_PIN_SCHEMA = cv.All( | ||||
|     weikai.WEIKAI_PIN_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(WeikaiGPIOPin), | ||||
|             cv.Required(CONF_WK2168_I2C): cv.use_id(WeikaiComponentI2C), | ||||
|         } | ||||
|     ), | ||||
|     weikai.validate_pin_mode, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2168_I2C, WK2168_PIN_SCHEMA) | ||||
| async def sc16is75x_pin_to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     parent = await cg.get_variable(config[CONF_WK2168_I2C]) | ||||
|     cg.add(var.set_parent(parent)) | ||||
|     num = config[CONF_NUMBER] | ||||
|     cg.add(var.set_pin(num)) | ||||
|     cg.add(var.set_inverted(config[CONF_INVERTED])) | ||||
|     cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) | ||||
|     return var | ||||
							
								
								
									
										62
									
								
								esphome/components/wk2168_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								esphome/components/wk2168_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import spi, weikai | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_INVERTED, | ||||
|     CONF_MODE, | ||||
|     CONF_NUMBER, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
| DEPENDENCIES = ["spi"] | ||||
| AUTO_LOAD = ["weikai", "weikai_spi"] | ||||
| MULTI_CONF = True | ||||
| CONF_WK2168_SPI = "wk2168_spi" | ||||
|  | ||||
| weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi") | ||||
| weikai_ns = cg.esphome_ns.namespace("weikai") | ||||
| WeikaiComponentSPI = weikai_spi_ns.class_( | ||||
|     "WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice | ||||
| ) | ||||
| WeikaiGPIOPin = weikai_ns.class_( | ||||
|     "WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentSPI) | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     weikai.WKBASE_SCHEMA.extend( | ||||
|         {cv.GenerateID(): cv.declare_id(WeikaiComponentSPI)} | ||||
|     ).extend(spi.spi_device_schema()), | ||||
|     weikai.check_channel_max_4, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     cg.add(var.set_name(str(config[CONF_ID]))) | ||||
|     await weikai.register_weikai(var, config) | ||||
|     await spi.register_spi_device(var, config) | ||||
|  | ||||
|  | ||||
| WK2168_PIN_SCHEMA = cv.All( | ||||
|     weikai.WEIKAI_PIN_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(WeikaiGPIOPin), | ||||
|             cv.Required(CONF_WK2168_SPI): cv.use_id(WeikaiComponentSPI), | ||||
|         }, | ||||
|     ), | ||||
|     weikai.validate_pin_mode, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2168_SPI, WK2168_PIN_SCHEMA) | ||||
| async def sc16is75x_pin_to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     parent = await cg.get_variable(config[CONF_WK2168_SPI]) | ||||
|     cg.add(var.set_parent(parent)) | ||||
|     num = config[CONF_NUMBER] | ||||
|     cg.add(var.set_pin(num)) | ||||
|     cg.add(var.set_inverted(config[CONF_INVERTED])) | ||||
|     cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) | ||||
|     return var | ||||
							
								
								
									
										30
									
								
								esphome/components/wk2204_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								esphome/components/wk2204_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c, weikai | ||||
| from esphome.const import CONF_ID | ||||
|  | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
| DEPENDENCIES = ["i2c"] | ||||
| AUTO_LOAD = ["weikai", "weikai_i2c"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c") | ||||
| WeikaiComponentI2C = weikai_i2c_ns.class_( | ||||
|     "WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     weikai.WKBASE_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(WeikaiComponentI2C), | ||||
|         } | ||||
|     ).extend(i2c.i2c_device_schema(0x2C)), | ||||
|     weikai.check_channel_max_4, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     cg.add(var.set_name(str(config[CONF_ID]))) | ||||
|     await weikai.register_weikai(var, config) | ||||
|     await i2c.register_i2c_device(var, config) | ||||
							
								
								
									
										30
									
								
								esphome/components/wk2204_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								esphome/components/wk2204_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import spi, weikai | ||||
| from esphome.const import CONF_ID | ||||
|  | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
| DEPENDENCIES = ["spi"] | ||||
| AUTO_LOAD = ["weikai", "weikai_spi"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi") | ||||
| WeikaiComponentSPI = weikai_spi_ns.class_( | ||||
|     "WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     weikai.WKBASE_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(WeikaiComponentSPI), | ||||
|         } | ||||
|     ).extend(spi.spi_device_schema()), | ||||
|     weikai.check_channel_max_4, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     cg.add(var.set_name(str(config[CONF_ID]))) | ||||
|     await weikai.register_weikai(var, config) | ||||
|     await spi.register_spi_device(var, config) | ||||
							
								
								
									
										64
									
								
								esphome/components/wk2212_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								esphome/components/wk2212_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import i2c, weikai | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_INVERTED, | ||||
|     CONF_MODE, | ||||
|     CONF_NUMBER, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
| DEPENDENCIES = ["i2c"] | ||||
| AUTO_LOAD = ["weikai", "weikai_i2c"] | ||||
| MULTI_CONF = True | ||||
| CONF_WK2212_I2C = "wk2212_i2c" | ||||
|  | ||||
| weikai_ns = cg.esphome_ns.namespace("weikai") | ||||
| weikai_i2c_ns = cg.esphome_ns.namespace("weikai_i2c") | ||||
| WeikaiComponentI2C = weikai_i2c_ns.class_( | ||||
|     "WeikaiComponentI2C", weikai.WeikaiComponent, i2c.I2CDevice | ||||
| ) | ||||
| WeikaiGPIOPin = weikai_ns.class_( | ||||
|     "WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentI2C) | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     weikai.WKBASE_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(WeikaiComponentI2C), | ||||
|         } | ||||
|     ).extend(i2c.i2c_device_schema(0x2C)), | ||||
|     weikai.check_channel_max_2, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     cg.add(var.set_name(str(config[CONF_ID]))) | ||||
|     await weikai.register_weikai(var, config) | ||||
|     await i2c.register_i2c_device(var, config) | ||||
|  | ||||
|  | ||||
| WK2212_PIN_SCHEMA = cv.All( | ||||
|     weikai.WEIKAI_PIN_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(WeikaiGPIOPin), | ||||
|             cv.Required(CONF_WK2212_I2C): cv.use_id(WeikaiComponentI2C), | ||||
|         } | ||||
|     ), | ||||
|     weikai.validate_pin_mode, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2212_I2C, WK2212_PIN_SCHEMA) | ||||
| async def sc16is75x_pin_to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     parent = await cg.get_variable(config[CONF_WK2212_I2C]) | ||||
|     cg.add(var.set_parent(parent)) | ||||
|     num = config[CONF_NUMBER] | ||||
|     cg.add(var.set_pin(num)) | ||||
|     cg.add(var.set_inverted(config[CONF_INVERTED])) | ||||
|     cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) | ||||
|     return var | ||||
							
								
								
									
										62
									
								
								esphome/components/wk2212_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								esphome/components/wk2212_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import spi, weikai | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_INVERTED, | ||||
|     CONF_MODE, | ||||
|     CONF_NUMBER, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@DrCoolZic"] | ||||
| DEPENDENCIES = ["spi"] | ||||
| AUTO_LOAD = ["weikai", "weikai_spi"] | ||||
| MULTI_CONF = True | ||||
| CONF_WK2212_SPI = "wk2212_spi" | ||||
|  | ||||
| weikai_ns = cg.esphome_ns.namespace("weikai") | ||||
| weikai_spi_ns = cg.esphome_ns.namespace("weikai_spi") | ||||
| WeikaiComponentSPI = weikai_spi_ns.class_( | ||||
|     "WeikaiComponentSPI", weikai.WeikaiComponent, spi.SPIDevice | ||||
| ) | ||||
| WeikaiGPIOPin = weikai_ns.class_( | ||||
|     "WeikaiGPIOPin", cg.GPIOPin, cg.Parented.template(WeikaiComponentSPI) | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     weikai.WKBASE_SCHEMA.extend( | ||||
|         {cv.GenerateID(): cv.declare_id(WeikaiComponentSPI)} | ||||
|     ).extend(spi.spi_device_schema()), | ||||
|     weikai.check_channel_max_2, | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     cg.add(var.set_name(str(config[CONF_ID]))) | ||||
|     await weikai.register_weikai(var, config) | ||||
|     await spi.register_spi_device(var, config) | ||||
|  | ||||
|  | ||||
| WK2212_PIN_SCHEMA = cv.All( | ||||
|     weikai.WEIKAI_PIN_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(WeikaiGPIOPin), | ||||
|             cv.Required(CONF_WK2212_SPI): cv.use_id(WeikaiComponentSPI), | ||||
|         }, | ||||
|     ), | ||||
|     weikai.validate_pin_mode, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @pins.PIN_SCHEMA_REGISTRY.register(CONF_WK2212_SPI, WK2212_PIN_SCHEMA) | ||||
| async def sc16is75x_pin_to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     parent = await cg.get_variable(config[CONF_WK2212_SPI]) | ||||
|     cg.add(var.set_parent(parent)) | ||||
|     num = config[CONF_NUMBER] | ||||
|     cg.add(var.set_pin(num)) | ||||
|     cg.add(var.set_inverted(config[CONF_INVERTED])) | ||||
|     cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) | ||||
|     return var | ||||
| @@ -253,6 +253,8 @@ CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support" | ||||
| CONF_ESPHOME = "esphome" | ||||
| CONF_ETHERNET = "ethernet" | ||||
| CONF_EVENT = "event" | ||||
| CONF_EVENT_TYPE = "event_type" | ||||
| CONF_EVENT_TYPES = "event_types" | ||||
| CONF_EXPIRE_AFTER = "expire_after" | ||||
| CONF_EXPORT_ACTIVE_ENERGY = "export_active_energy" | ||||
| CONF_EXPORT_REACTIVE_ENERGY = "export_reactive_energy" | ||||
| @@ -519,6 +521,7 @@ CONF_ON_DOUBLE_CLICK = "on_double_click" | ||||
| CONF_ON_ENROLLMENT_DONE = "on_enrollment_done" | ||||
| CONF_ON_ENROLLMENT_FAILED = "on_enrollment_failed" | ||||
| CONF_ON_ENROLLMENT_SCAN = "on_enrollment_scan" | ||||
| CONF_ON_EVENT = "on_event" | ||||
| CONF_ON_FINGER_SCAN_INVALID = "on_finger_scan_invalid" | ||||
| CONF_ON_FINGER_SCAN_MATCHED = "on_finger_scan_matched" | ||||
| CONF_ON_FINGER_SCAN_MISPLACED = "on_finger_scan_misplaced" | ||||
| @@ -1027,6 +1030,7 @@ DEVICE_CLASS_AWNING = "awning" | ||||
| DEVICE_CLASS_BATTERY = "battery" | ||||
| DEVICE_CLASS_BATTERY_CHARGING = "battery_charging" | ||||
| DEVICE_CLASS_BLIND = "blind" | ||||
| DEVICE_CLASS_BUTTON = "button" | ||||
| DEVICE_CLASS_CARBON_DIOXIDE = "carbon_dioxide" | ||||
| DEVICE_CLASS_CARBON_MONOXIDE = "carbon_monoxide" | ||||
| DEVICE_CLASS_COLD = "cold" | ||||
| @@ -1039,6 +1043,7 @@ DEVICE_CLASS_DATA_SIZE = "data_size" | ||||
| DEVICE_CLASS_DATE = "date" | ||||
| DEVICE_CLASS_DISTANCE = "distance" | ||||
| DEVICE_CLASS_DOOR = "door" | ||||
| DEVICE_CLASS_DOORBELL = "doorbell" | ||||
| DEVICE_CLASS_DURATION = "duration" | ||||
| DEVICE_CLASS_EMPTY = "" | ||||
| DEVICE_CLASS_ENERGY = "energy" | ||||
|   | ||||
| @@ -54,12 +54,18 @@ | ||||
| #ifdef USE_LOCK | ||||
| #include "esphome/components/lock/lock.h" | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| #include "esphome/components/valve/valve.h" | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| #include "esphome/components/media_player/media_player.h" | ||||
| #endif | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
| #include "esphome/components/alarm_control_panel/alarm_control_panel.h" | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
| #include "esphome/components/event/event.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
|  | ||||
| @@ -147,6 +153,10 @@ class Application { | ||||
|   void register_lock(lock::Lock *a_lock) { this->locks_.push_back(a_lock); } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_VALVE | ||||
|   void register_valve(valve::Valve *valve) { this->valves_.push_back(valve); } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   void register_media_player(media_player::MediaPlayer *media_player) { this->media_players_.push_back(media_player); } | ||||
| #endif | ||||
| @@ -157,6 +167,10 @@ class Application { | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
|   void register_event(event::Event *event) { this->events_.push_back(event); } | ||||
| #endif | ||||
|  | ||||
|   /// Register the component in this Application instance. | ||||
|   template<class C> C *register_component(C *c) { | ||||
|     static_assert(std::is_base_of<Component, C>::value, "Only Component subclasses can be registered"); | ||||
| @@ -348,6 +362,15 @@ class Application { | ||||
|     return nullptr; | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   const std::vector<valve::Valve *> &get_valves() { return this->valves_; } | ||||
|   valve::Valve *get_valve_by_key(uint32_t key, bool include_internal = false) { | ||||
|     for (auto *obj : this->valves_) | ||||
|       if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) | ||||
|         return obj; | ||||
|     return nullptr; | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   const std::vector<media_player::MediaPlayer *> &get_media_players() { return this->media_players_; } | ||||
|   media_player::MediaPlayer *get_media_player_by_key(uint32_t key, bool include_internal = false) { | ||||
| @@ -370,6 +393,16 @@ class Application { | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
|   const std::vector<event::Event *> &get_events() { return this->events_; } | ||||
|   event::Event *get_event_by_key(uint32_t key, bool include_internal = false) { | ||||
|     for (auto *obj : this->events_) | ||||
|       if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) | ||||
|         return obj; | ||||
|     return nullptr; | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   Scheduler scheduler; | ||||
|  | ||||
|  protected: | ||||
| @@ -393,6 +426,9 @@ class Application { | ||||
| #ifdef USE_BUTTON | ||||
|   std::vector<button::Button *> buttons_{}; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   std::vector<event::Event *> events_{}; | ||||
| #endif | ||||
| #ifdef USE_SENSOR | ||||
|   std::vector<sensor::Sensor *> sensors_{}; | ||||
| #endif | ||||
| @@ -429,6 +465,9 @@ class Application { | ||||
| #ifdef USE_LOCK | ||||
|   std::vector<lock::Lock *> locks_{}; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   std::vector<valve::Valve *> valves_{}; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   std::vector<media_player::MediaPlayer *> media_players_{}; | ||||
| #endif | ||||
|   | ||||
| @@ -62,6 +62,9 @@ struct Color { | ||||
|     return Color(esp_scale8(this->red, scale), esp_scale8(this->green, scale), esp_scale8(this->blue, scale), | ||||
|                  esp_scale8(this->white, scale)); | ||||
|   } | ||||
|   inline Color operator~() const ESPHOME_ALWAYS_INLINE { | ||||
|     return Color(255 - this->red, 255 - this->green, 255 - this->blue); | ||||
|   } | ||||
|   inline Color &operator*=(uint8_t scale) ESPHOME_ALWAYS_INLINE { | ||||
|     this->red = esp_scale8(this->red, scale); | ||||
|     this->green = esp_scale8(this->green, scale); | ||||
|   | ||||
| @@ -277,6 +277,21 @@ void ComponentIterator::advance() { | ||||
|       } | ||||
|       break; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|     case IteratorState::VALVE: | ||||
|       if (this->at_ >= App.get_valves().size()) { | ||||
|         advance_platform = true; | ||||
|       } else { | ||||
|         auto *valve = App.get_valves()[this->at_]; | ||||
|         if (valve->is_internal() && !this->include_internal_) { | ||||
|           success = true; | ||||
|           break; | ||||
|         } else { | ||||
|           success = this->on_valve(valve); | ||||
|         } | ||||
|       } | ||||
|       break; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|     case IteratorState::MEDIA_PLAYER: | ||||
|       if (this->at_ >= App.get_media_players().size()) { | ||||
| @@ -306,6 +321,21 @@ void ComponentIterator::advance() { | ||||
|         } | ||||
|       } | ||||
|       break; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|     case IteratorState::EVENT: | ||||
|       if (this->at_ >= App.get_events().size()) { | ||||
|         advance_platform = true; | ||||
|       } else { | ||||
|         auto *event = App.get_events()[this->at_]; | ||||
|         if (event->is_internal() && !this->include_internal_) { | ||||
|           success = true; | ||||
|           break; | ||||
|         } else { | ||||
|           success = this->on_event(event); | ||||
|         } | ||||
|       } | ||||
|       break; | ||||
| #endif | ||||
|     case IteratorState::MAX: | ||||
|       if (this->on_end()) { | ||||
|   | ||||
| @@ -72,11 +72,17 @@ class ComponentIterator { | ||||
| #ifdef USE_LOCK | ||||
|   virtual bool on_lock(lock::Lock *a_lock) = 0; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   virtual bool on_valve(valve::Valve *valve) = 0; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   virtual bool on_media_player(media_player::MediaPlayer *media_player); | ||||
| #endif | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   virtual bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) = 0; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   virtual bool on_event(event::Event *event) = 0; | ||||
| #endif | ||||
|   virtual bool on_end(); | ||||
|  | ||||
| @@ -135,11 +141,17 @@ class ComponentIterator { | ||||
| #ifdef USE_LOCK | ||||
|     LOCK, | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|     VALVE, | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|     MEDIA_PLAYER, | ||||
| #endif | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|     ALARM_CONTROL_PANEL, | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|     EVENT, | ||||
| #endif | ||||
|     MAX, | ||||
|   } state_{IteratorState::NONE}; | ||||
|   | ||||
| @@ -91,6 +91,12 @@ void Controller::setup_controller(bool include_internal) { | ||||
|       obj->add_on_state_callback([this, obj]() { this->on_lock_update(obj); }); | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   for (auto *obj : App.get_valves()) { | ||||
|     if (include_internal || !obj->is_internal()) | ||||
|       obj->add_on_state_callback([this, obj]() { this->on_valve_update(obj); }); | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   for (auto *obj : App.get_media_players()) { | ||||
|     if (include_internal || !obj->is_internal()) | ||||
| @@ -103,6 +109,12 @@ void Controller::setup_controller(bool include_internal) { | ||||
|       obj->add_on_state_callback([this, obj]() { this->on_alarm_control_panel_update(obj); }); | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   for (auto *obj : App.get_events()) { | ||||
|     if (include_internal || !obj->is_internal()) | ||||
|       obj->add_on_event_callback([this, obj](const std::string &event_type) { this->on_event(obj, event_type); }); | ||||
|   } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -46,12 +46,18 @@ | ||||
| #ifdef USE_LOCK | ||||
| #include "esphome/components/lock/lock.h" | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
| #include "esphome/components/valve/valve.h" | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
| #include "esphome/components/media_player/media_player.h" | ||||
| #endif | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
| #include "esphome/components/alarm_control_panel/alarm_control_panel.h" | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
| #include "esphome/components/event/event.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
|  | ||||
| @@ -100,12 +106,18 @@ class Controller { | ||||
| #ifdef USE_LOCK | ||||
|   virtual void on_lock_update(lock::Lock *obj){}; | ||||
| #endif | ||||
| #ifdef USE_VALVE | ||||
|   virtual void on_valve_update(valve::Valve *obj){}; | ||||
| #endif | ||||
| #ifdef USE_MEDIA_PLAYER | ||||
|   virtual void on_media_player_update(media_player::MediaPlayer *obj){}; | ||||
| #endif | ||||
| #ifdef USE_ALARM_CONTROL_PANEL | ||||
|   virtual void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj){}; | ||||
| #endif | ||||
| #ifdef USE_EVENT | ||||
|   virtual void on_event(event::Event *obj, const std::string &event_type){}; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #define USE_CLIMATE | ||||
| #define USE_COVER | ||||
| #define USE_DEEP_SLEEP | ||||
| #define USE_EVENT | ||||
| #define USE_FAN | ||||
| #define USE_GRAPH | ||||
| #define USE_HOMEASSISTANT_TIME | ||||
| @@ -54,6 +55,7 @@ | ||||
| #define USE_TIME | ||||
| #define USE_TOUCHSCREEN | ||||
| #define USE_UART_DEBUGGER | ||||
| #define USE_VALVE | ||||
| #define USE_WIFI | ||||
| #define USE_WIFI_AP | ||||
| #define USE_GRAPHICAL_DISPLAY_MENU | ||||
|   | ||||
| @@ -62,6 +62,24 @@ class GPIOPin { | ||||
|   virtual bool is_internal() { return false; } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * A pin to replace those that don't exist. | ||||
|  */ | ||||
| class NullPin : public GPIOPin { | ||||
|  public: | ||||
|   void setup() override {} | ||||
|  | ||||
|   void pin_mode(gpio::Flags _) override {} | ||||
|  | ||||
|   bool digital_read() override { return false; } | ||||
|  | ||||
|   void digital_write(bool _) override {} | ||||
|  | ||||
|   std::string dump_summary() const override { return {"Not used"}; } | ||||
| }; | ||||
|  | ||||
| static GPIOPin *const NULL_PIN = new NullPin(); | ||||
|  | ||||
| /// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions) | ||||
| class ISRInternalGPIOPin { | ||||
|  public: | ||||
|   | ||||
| @@ -443,6 +443,103 @@ int8_t step_to_accuracy_decimals(float step) { | ||||
|   return str.length() - dot_pos - 1; | ||||
| } | ||||
|  | ||||
| static inline bool is_base64(char c) { return (isalnum(c) || (c == '+') || (c == '/')); } | ||||
|  | ||||
| std::string base64_encode(const std::vector<uint8_t> &buf) { return base64_encode(buf.data(), buf.size()); } | ||||
|  | ||||
| std::string base64_encode(const char *buf, unsigned int buf_len) { | ||||
|   std::string ret; | ||||
|   int i = 0; | ||||
|   int j = 0; | ||||
|   char char_array_3[3]; | ||||
|   char char_array_4[4]; | ||||
|  | ||||
|   while (buf_len--) { | ||||
|     char_array_3[i++] = *(buf++); | ||||
|     if (i == 3) { | ||||
|       char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; | ||||
|       char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); | ||||
|       char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); | ||||
|       char_array_4[3] = char_array_3[2] & 0x3f; | ||||
|  | ||||
|       for (i = 0; (i < 4); i++) | ||||
|         ret += BASE64_CHARS[char_array_4[i]]; | ||||
|       i = 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (i) { | ||||
|     for (j = i; j < 3; j++) | ||||
|       char_array_3[j] = '\0'; | ||||
|  | ||||
|     char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; | ||||
|     char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); | ||||
|     char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); | ||||
|     char_array_4[3] = char_array_3[2] & 0x3f; | ||||
|  | ||||
|     for (j = 0; (j < i + 1); j++) | ||||
|       ret += BASE64_CHARS[char_array_4[j]]; | ||||
|  | ||||
|     while ((i++ < 3)) | ||||
|       ret += '='; | ||||
|   } | ||||
|  | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| size_t base64_decode(const std::string &encoded_string, uint8_t *buf, size_t buf_len) { | ||||
|   std::vector<uint8_t> decoded = base64_decode(encoded_string); | ||||
|   if (decoded.size() > buf_len) { | ||||
|     ESP_LOGW(TAG, "Base64 decode: buffer too small, truncating"); | ||||
|     decoded.resize(buf_len); | ||||
|   } | ||||
|   memcpy(buf, decoded.data(), decoded.size()); | ||||
|   return decoded.size(); | ||||
| } | ||||
|  | ||||
| std::vector<uint8_t> base64_decode(const std::string &encoded_string) { | ||||
|   int in_len = encoded_string.size(); | ||||
|   int i = 0; | ||||
|   int j = 0; | ||||
|   int in = 0; | ||||
|   uint8_t char_array_4[4], char_array_3[3]; | ||||
|   std::vector<uint8_t> ret; | ||||
|  | ||||
|   while (in_len-- && (encoded_string[in] != '=') && is_base64(encoded_string[in])) { | ||||
|     char_array_4[i++] = encoded_string[in]; | ||||
|     in++; | ||||
|     if (i == 4) { | ||||
|       for (i = 0; i < 4; i++) | ||||
|         char_array_4[i] = BASE64_CHARS.find(char_array_4[i]); | ||||
|  | ||||
|       char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); | ||||
|       char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); | ||||
|       char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; | ||||
|  | ||||
|       for (i = 0; (i < 3); i++) | ||||
|         ret.push_back(char_array_3[i]); | ||||
|       i = 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (i) { | ||||
|     for (j = i; j < 4; j++) | ||||
|       char_array_4[j] = 0; | ||||
|  | ||||
|     for (j = 0; j < 4; j++) | ||||
|       char_array_4[j] = BASE64_CHARS.find(char_array_4[j]); | ||||
|  | ||||
|     char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); | ||||
|     char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); | ||||
|     char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; | ||||
|  | ||||
|     for (j = 0; (j < i - 1); j++) | ||||
|       ret.push_back(char_array_3[j]); | ||||
|   } | ||||
|  | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| // Colors | ||||
|  | ||||
| float gamma_correct(float value, float gamma) { | ||||
|   | ||||
| @@ -438,6 +438,16 @@ std::string value_accuracy_to_string(float value, int8_t accuracy_decimals); | ||||
| /// Derive accuracy in decimals from an increment step. | ||||
| int8_t step_to_accuracy_decimals(float step); | ||||
|  | ||||
| static const std::string BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||||
|                                         "abcdefghijklmnopqrstuvwxyz" | ||||
|                                         "0123456789+/"; | ||||
|  | ||||
| std::string base64_encode(const uint8_t *buf, size_t buf_len); | ||||
| std::string base64_encode(const std::vector<uint8_t> &buf); | ||||
|  | ||||
| std::vector<uint8_t> base64_decode(const std::string &encoded_string); | ||||
| size_t base64_decode(std::string const &encoded_string, uint8_t *buf, size_t buf_len); | ||||
|  | ||||
| ///@} | ||||
|  | ||||
| /// @name Colors | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import paho.mqtt.client as mqtt | ||||
|  | ||||
| from esphome.const import ( | ||||
|     CONF_BROKER, | ||||
|     CONF_CERTIFICATE_AUTHORITY, | ||||
|     CONF_DISCOVERY_PREFIX, | ||||
|     CONF_ESPHOME, | ||||
|     CONF_LOG_TOPIC, | ||||
| @@ -99,7 +100,9 @@ def prepare( | ||||
|     elif username: | ||||
|         client.username_pw_set(username, password) | ||||
|  | ||||
|     if config[CONF_MQTT].get(CONF_SSL_FINGERPRINTS): | ||||
|     if config[CONF_MQTT].get(CONF_SSL_FINGERPRINTS) or config[CONF_MQTT].get( | ||||
|         CONF_CERTIFICATE_AUTHORITY | ||||
|     ): | ||||
|         if sys.version_info >= (2, 7, 13): | ||||
|             tls_version = ssl.PROTOCOL_TLS  # pylint: disable=no-member | ||||
|         else: | ||||
|   | ||||
| @@ -321,8 +321,9 @@ class ESPHomeLoaderMixin: | ||||
|             file, vars = node.value, None | ||||
|  | ||||
|         result = _load_yaml_internal(self._rel_path(file)) | ||||
|         if vars: | ||||
|             result = substitute_vars(result, vars) | ||||
|         if not vars: | ||||
|             vars = {} | ||||
|         result = substitute_vars(result, vars) | ||||
|         return result | ||||
|  | ||||
|     @_add_data_ref | ||||
|   | ||||
| @@ -13,7 +13,7 @@ platformio==6.1.13  # When updating platformio, also update Dockerfile | ||||
| esptool==4.7.0 | ||||
| click==8.1.7 | ||||
| esphome-dashboard==20240412.0 | ||||
| aioesphomeapi==24.0.0 | ||||
| aioesphomeapi==24.3.0 | ||||
| zeroconf==0.132.2 | ||||
| python-magic==0.4.27 | ||||
| ruamel.yaml==0.18.6 # dashboard_import | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # Useful stuff when working in a development environment | ||||
| clang-format==13.0.1 | ||||
| clang-tidy==14.0.6 | ||||
| yamllint==1.35.1 | ||||
| clang-format==13.0.1  # also change in .pre-commit-config.yaml and Dockerfile when updating | ||||
| clang-tidy==14.0.6  # When updating clang-tidy, also update Dockerfile | ||||
| yamllint==1.35.1  # also change in .pre-commit-config.yaml when updating | ||||
|   | ||||
| @@ -624,6 +624,7 @@ def lint_trailing_whitespace(fname, match): | ||||
|         "esphome/components/datetime/date_entity.h", | ||||
|         "esphome/components/datetime/time_entity.h", | ||||
|         "esphome/components/display/display.h", | ||||
|         "esphome/components/event/event.h", | ||||
|         "esphome/components/fan/fan.h", | ||||
|         "esphome/components/i2c/i2c.h", | ||||
|         "esphome/components/lock/lock.h", | ||||
| @@ -638,6 +639,7 @@ def lint_trailing_whitespace(fname, match): | ||||
|         "esphome/components/stepper/stepper.h", | ||||
|         "esphome/components/switch/switch.h", | ||||
|         "esphome/components/text_sensor/text_sensor.h", | ||||
|         "esphome/components/valve/valve.h", | ||||
|         "esphome/core/component.h", | ||||
|         "esphome/core/gpio.h", | ||||
|         "esphome/core/log.h", | ||||
|   | ||||
							
								
								
									
										9
									
								
								tests/components/event/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/components/event/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| event: | ||||
|   - platform: template | ||||
|     name: Event | ||||
|     id: some_event | ||||
|     event_types: | ||||
|       - template_event_type1 | ||||
|       - template_event_type2 | ||||
|     on_event: | ||||
|       - logger.log: Event fired | ||||
							
								
								
									
										9
									
								
								tests/components/event/test.esp32-c3.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/components/event/test.esp32-c3.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| event: | ||||
|   - platform: template | ||||
|     name: Event | ||||
|     id: some_event | ||||
|     event_types: | ||||
|       - template_event_type1 | ||||
|       - template_event_type2 | ||||
|     on_event: | ||||
|       - logger.log: Event fired | ||||
							
								
								
									
										9
									
								
								tests/components/event/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/components/event/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| event: | ||||
|   - platform: template | ||||
|     name: Event | ||||
|     id: some_event | ||||
|     event_types: | ||||
|       - template_event_type1 | ||||
|       - template_event_type2 | ||||
|     on_event: | ||||
|       - logger.log: Event fired | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user