11#include " api_connection.hpp"
2- #include < print>
32#include " api.pb.h"
43#include " make_unexpected_result.hpp"
54
5+ #include < print>
6+
67namespace asio = boost::asio;
78namespace this_coro = asio::this_coro;
89
10+ #define REQUIRE_SUCCESS (async_operation ) \
11+ { \
12+ auto &&result = async_operation; \
13+ if (not result.has_value ()) \
14+ { \
15+ co_return std::unexpected (result.error ()); \
16+ } \
17+ }
18+
919namespace cppesphomeapi
1020{
1121ApiConnection::ApiConnection (std::string hostname,
@@ -28,28 +38,116 @@ AsyncResult<void> ApiConnection::connect()
2838 co_await socket_.async_connect (resolved->endpoint ());
2939 socket_.set_option (asio::socket_base::keep_alive{true });
3040
31- auto send_result = co_await send_message_hello ();
41+ REQUIRE_SUCCESS (co_await send_message_hello ());
42+ REQUIRE_SUCCESS (co_await send_message_connect ());
3243
33- co_return send_result;
44+ co_return Result<void >{};
45+ }
46+
47+ AsyncResult<void > ApiConnection::disconnect ()
48+ {
49+ proto::DisconnectRequest request;
50+ REQUIRE_SUCCESS (co_await send_message (request));
51+ REQUIRE_SUCCESS (co_await receive_message<proto::DisconnectResponse>());
52+ co_return Result<void >{};
3453}
3554
3655AsyncResult<void > ApiConnection::send_message_hello ()
3756{
38- cppesphomeapi::proto::HelloRequest hello_request;
39-
40- hello_request.ParseFromArray (nullptr , 0 );
41- hello_request.set_client_info (std::string{" cppapi" });
42- co_await send_message (hello_request);
43- const auto msg_promise = co_await receive_message<cppesphomeapi::proto::HelloResponse>();
44-
45- std::println (" Got esphome device \" {}\" : Version {}.{}" ,
46- msg_promise->name (),
47- msg_promise->api_version_major (),
48- msg_promise->api_version_minor ());
49- // todo: do something with the message.
57+ proto::HelloRequest request;
58+ request.set_client_info (std::string{" cppapi" });
59+ REQUIRE_SUCCESS (co_await send_message (request));
60+ const auto response = co_await receive_message<proto::HelloResponse>();
61+ REQUIRE_SUCCESS (response);
62+ device_name_ = response->name ();
63+ api_version_ = ApiVersion{.major = response->api_version_major (), .minor = response->api_version_minor ()};
5064 co_return Result<void >{};
5165}
5266
67+ AsyncResult<void > ApiConnection::send_message_connect ()
68+ {
69+ proto::ConnectRequest request;
70+ request.set_password (password_);
71+ REQUIRE_SUCCESS (co_await send_message (request));
72+ const auto response = co_await receive_message<proto::ConnectResponse>();
73+ if (response->invalid_password ())
74+ {
75+ co_return make_unexpected_result (ApiErrorCode::AuthentificationError, " Invalid password" );
76+ }
77+ co_return Result<void >{};
78+ }
79+
80+ AsyncResult<DeviceInfo> ApiConnection::request_device_info ()
81+ {
82+ proto::DeviceInfoRequest device_request{};
83+ REQUIRE_SUCCESS (co_await send_message (device_request));
84+ const auto response = co_await receive_message<proto::DeviceInfoResponse>();
85+ REQUIRE_SUCCESS (response);
86+
87+ co_return DeviceInfo{
88+ .uses_password = response->uses_password (),
89+ .has_deep_sleep = response->has_deep_sleep (),
90+ .name = response->name (),
91+ .friendly_name = response->friendly_name (),
92+ .mac_address = response->mac_address (),
93+ .compilation_time = response->compilation_time (), // todo: maybe parse directly into std::chrono?
94+ .model = response->model (),
95+ .manufacturer = response->manufacturer (),
96+ .esphome_version = response->esphome_version (),
97+ .webserver_port = static_cast <uint16_t >(response->webserver_port ()),
98+ .suggested_area = response->suggested_area (),
99+ };
100+ }
101+
102+ AsyncResult<std::vector<EntityInfo>> ApiConnection::request_entities_and_services ()
103+ {
104+ proto::ListEntitiesRequest request;
105+ REQUIRE_SUCCESS (co_await send_message (request));
106+ const auto messages = co_await receive_messages<proto::ListEntitiesDoneResponse,
107+ proto::ListEntitiesAlarmControlPanelResponse,
108+ proto::ListEntitiesBinarySensorResponse,
109+ proto::ListEntitiesButtonResponse,
110+ proto::ListEntitiesCameraResponse,
111+ proto::ListEntitiesClimateResponse,
112+ proto::ListEntitiesCoverResponse,
113+ proto::ListEntitiesDateResponse,
114+ proto::ListEntitiesDateTimeResponse,
115+ proto::ListEntitiesEventResponse,
116+ proto::ListEntitiesFanResponse,
117+ proto::ListEntitiesLightResponse,
118+ proto::ListEntitiesLockResponse,
119+ proto::ListEntitiesMediaPlayerResponse,
120+ proto::ListEntitiesNumberResponse,
121+ proto::ListEntitiesSelectResponse,
122+ proto::ListEntitiesSensorResponse,
123+ proto::ListEntitiesServicesResponse,
124+ proto::ListEntitiesSwitchResponse,
125+ proto::ListEntitiesTextResponse,
126+ proto::ListEntitiesTextSensorResponse,
127+ proto::ListEntitiesTimeResponse,
128+ proto::ListEntitiesUpdateResponse,
129+ proto::ListEntitiesValveResponse>();
130+ REQUIRE_SUCCESS (messages);
131+
132+ for (auto &&msg : messages.value ())
133+ {
134+ std::println (" GOT LIST .{}" , std::visit ([](auto &&msg) { return msg.key (); }, msg));
135+ }
136+ co_return std::vector<EntityInfo>{};
137+ }
138+
139+ AsyncResult<void > ApiConnection::light_command (LightCommand light_command)
140+ {
141+ proto::LightCommandRequest request{};
142+ request.set_key (light_command.key );
143+
144+ if (light_command.effect .has_value ())
145+ {
146+ request.set_effect (std::move (light_command.effect .value ()));
147+ }
148+ co_return co_await send_message (request);
149+ }
150+
53151AsyncResult<void > ApiConnection::send_message (const google::protobuf::Message &message)
54152{
55153 const auto packet = plain_text_serialize (message);
@@ -68,4 +166,14 @@ AsyncResult<void> ApiConnection::send_message(const google::protobuf::Message &m
68166 }
69167 co_return std::unexpected (packet.error ());
70168}
169+
170+ const std::optional<ApiVersion> &ApiConnection::api_version () const
171+ {
172+ return api_version_;
173+ }
174+
175+ const std::string &ApiConnection::device_name () const
176+ {
177+ return device_name_;
178+ }
71179} // namespace cppesphomeapi
0 commit comments