-
-
Notifications
You must be signed in to change notification settings - Fork 28
Development of Athom Homey Pro integration into NSPanelManager #338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: devel
Are you sure you want to change the base?
Changes from all commits
61cfb58
8a2dab0
86a02c0
38c74ee
4e2bded
ffb1b24
882b8f8
65379fd
06331df
af7d98b
cd56217
908dbc3
59b3677
9f7e436
4b299c0
970663d
7e091bb
b5dca88
73182ef
d934211
7fc001c
ca6e347
d952529
ae6acdf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,6 +34,11 @@ class ButtonEntity : public MqttManagerEntity { | |
| */ | ||
| uint16_t get_id(); | ||
|
|
||
| /** | ||
| * Get the on/off state of the switch. | ||
| */ | ||
| bool get_state(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function has not been implemented in the .cpp file. |
||
|
|
||
| /** | ||
| * Get the friendly name for the button. | ||
| */ | ||
|
|
@@ -96,6 +101,9 @@ class ButtonEntity : public MqttManagerEntity { | |
| std::mutex _entity_data_mutex; | ||
| nlohmann::json _entity_data; | ||
|
|
||
| bool _current_state; | ||
| bool _requested_state; | ||
|
|
||
| boost::signals2::signal<void(ButtonEntity *)> _button_destroyed_callbacks; | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| #include "homey_button.hpp" | ||
| #include "database_manager/database_manager.hpp" | ||
| #include "entity/entity.hpp" | ||
| #include "web_helper/WebHelper.hpp" | ||
| #include "mqtt_manager/mqtt_manager.hpp" | ||
| #include "mqtt_manager_config/mqtt_manager_config.hpp" | ||
| #include <boost/algorithm/string/predicate.hpp> | ||
| #include <boost/bind.hpp> | ||
| #include <boost/exception/diagnostic_information.hpp> | ||
| #include <chrono> | ||
| #include <cstdint> | ||
| #include <homey_manager/homey_manager.hpp> | ||
| #include <nlohmann/json.hpp> | ||
| #include <spdlog/spdlog.h> | ||
| #include <string> | ||
| #include <switch/switch.hpp> | ||
|
|
||
| HomeyButton::HomeyButton(uint32_t button_id) : ButtonEntity(button_id) | ||
| { | ||
| if (this->_controller != MQTT_MANAGER_ENTITY_CONTROLLER::HOMEY) | ||
| { | ||
| SPDLOG_ERROR("HomeyButton has not been recognized as controlled by HOMEY. Will stop processing button."); | ||
| return; | ||
| } | ||
|
|
||
| nlohmann::json entity_data; | ||
| try | ||
| { | ||
| auto button_entity = database_manager::database.get<database_manager::Entity>(this->_id); | ||
| entity_data = button_entity.get_entity_data_json(); | ||
| } | ||
| catch (const std::exception &e) | ||
| { | ||
| SPDLOG_ERROR("Failed to load button {}: {}", this->_id, e.what()); | ||
| return; | ||
| } | ||
|
|
||
| if (entity_data.contains("homey_device_id")) | ||
| { | ||
| this->_homey_device_id = entity_data["homey_device_id"]; | ||
| } | ||
| else | ||
| { | ||
| SPDLOG_ERROR("No homey_device_id defined for Button {}::{}", this->_id, this->_name); | ||
| return; | ||
| } | ||
|
|
||
| SPDLOG_DEBUG("Loaded Homey button {}::{}, device ID: {}", this->_id, this->_name, this->_homey_device_id); | ||
| HomeyManager::attach_event_observer(this->_homey_device_id, boost::bind(&HomeyButton::homey_event_callback, this, _1)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you've written below, buttons are one way only. Perhaps comment out this line and the on in the destructor that attaches event observers as they are not needed? |
||
| } | ||
|
|
||
| HomeyButton::~HomeyButton() | ||
| { | ||
| HomeyManager::detach_event_observer(this->_homey_device_id, boost::bind(&HomeyButton::homey_event_callback, this, _1)); | ||
| } | ||
|
|
||
| void HomeyButton::send_state_update_to_controller() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason this is done over HTTP PUT? Does Homey not support doing this over websockets? Websockets would be preferable as we reuse an already established connection and that is therefore faster. |
||
| { | ||
| SPDLOG_DEBUG("Homey button {}::{} send_state_update_to_controller (trigger button press). State: {}", this->_id, this->_name, this->_requested_state); | ||
|
|
||
| // Get Homey connection settings | ||
| auto homey_address = MqttManagerConfig::get_setting_with_default<std::string>(MQTT_MANAGER_SETTING::HOMEY_ADDRESS); | ||
| auto homey_token = MqttManagerConfig::get_setting_with_default<std::string>(MQTT_MANAGER_SETTING::HOMEY_TOKEN); | ||
|
|
||
| if (homey_address.empty() || homey_token.empty()) | ||
| { | ||
| SPDLOG_ERROR("Homey address or token not configured for button {}::{}", this->_id, this->_name); | ||
| return; | ||
| } | ||
|
|
||
| // Construct URL: http://{homey_address}/api/manager/devices/device/{device_id}/capability/onoff | ||
| std::string url = fmt::format("http://{}/api/manager/devices/device/{}/capability/onoff", homey_address, this->_homey_device_id); | ||
|
|
||
| // Create request body - button trigger uses null value | ||
| nlohmann::json request_body; | ||
| request_body["value"] = this->_requested_state; | ||
|
|
||
| // Send HTTP PUT request with bearer token authentication | ||
| try | ||
| { | ||
| // Create header strings with proper lifetime management | ||
| std::string auth_header = fmt::format("Authorization: Bearer {}", homey_token); | ||
| std::list<const char *> headers = { | ||
| auth_header.c_str(), | ||
| "Content-Type: application/json"}; | ||
|
|
||
| std::string response_data; | ||
| std::string put_data = request_body.dump(); | ||
|
|
||
| if (WebHelper::perform_put_request(&url, &response_data, &headers, &put_data)) | ||
| { | ||
| SPDLOG_DEBUG("Homey button {}::{} trigger response: {}", this->_id, this->_name, response_data); | ||
| } | ||
| else | ||
| { | ||
| SPDLOG_ERROR("Failed to trigger Homey button {}::{}", this->_id, this->_name); | ||
| } | ||
| } | ||
| catch (const std::exception &e) | ||
| { | ||
| SPDLOG_ERROR("Failed to trigger Homey button {}::{}: {}", this->_id, this->_name, e.what()); | ||
| } | ||
|
|
||
| // Buttons don't have persistent state, so no optimistic mode handling needed | ||
| } | ||
|
|
||
| void HomeyButton::homey_event_callback(nlohmann::json data) | ||
| { | ||
| try | ||
| { | ||
| // Homey WebSocket sends: {"id": "device-uuid", "capabilitiesObj": {...}} | ||
| // Buttons typically don't send state updates, but we'll handle them if they do | ||
| SPDLOG_DEBUG("Got event update for Homey button {}::{}.", this->_id, this->_name); | ||
|
|
||
| // Buttons are typically one-way (trigger only), so no state to process | ||
| // But we keep this callback for potential future use | ||
| } | ||
| catch (std::exception &e) | ||
| { | ||
| SPDLOG_ERROR("Caught exception when processing Homey event for button {}::{}: {}", | ||
| this->_id, this->_name, boost::diagnostic_information(e, true)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #ifndef MQTT_MANAGER_HOMEY_BUTTON | ||
| #define MQTT_MANAGER_HOMEY_BUTTON | ||
|
|
||
| #include "button.hpp" | ||
| #include <string> | ||
|
|
||
| class HomeyButton : public ButtonEntity | ||
| { | ||
| public: | ||
| HomeyButton(uint32_t button_id); | ||
| ~HomeyButton(); | ||
| void send_state_update_to_controller(); | ||
| void homey_event_callback(nlohmann::json event_data); | ||
|
|
||
| private: | ||
| std::string _homey_device_id; | ||
| }; | ||
|
|
||
| #endif // !MQTT_MANAGER_HOMEY_BUTTON |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| #include <cstdint> | ||
|
|
||
| class EntityIcons { | ||
| class EntityIcons | ||
| { | ||
| public: | ||
| // Entity Icons | ||
| static constexpr const char *entity_icon_switch_on = "s"; | ||
|
|
@@ -11,6 +12,7 @@ class EntityIcons { | |
| static constexpr const char *save_icon = "w"; | ||
| static constexpr const char *home_assistant_icon = "x"; | ||
| static constexpr const char *openhab_icon = "y"; | ||
| static constexpr const char *homey_icon = "{"; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you added this to the font? This is somewhat specially managed by @cablesandcoffee. It needs to be added to the font used by the NSPanel firmware in order to show up correctly. |
||
|
|
||
| // Thermostat Icons | ||
| static constexpr const char *heating = "!"; | ||
|
|
@@ -37,7 +39,8 @@ class EntityIcons { | |
| static constexpr const char *fan3 = "6"; | ||
| }; | ||
|
|
||
| class GUI_Colors { | ||
| class GUI_Colors | ||
| { | ||
| public: | ||
| static constexpr const uint16_t icon_color_off = 65535; | ||
| static constexpr const uint16_t icon_color_on = 65024; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't see that this is used anywhere. Is there a reason for it being here?