Skip to content

Commit 8fb0eca

Browse files
📝 Add docstrings to Add-fan-speed-and-oscillation-feature
Docstrings generation was requested by @mikailofficial. * #54 (comment) The following files were modified: * `components/homekit/fan.hpp`
1 parent 7089a4c commit 8fb0eca

1 file changed

Lines changed: 135 additions & 10 deletions

File tree

components/homekit/fan.hpp

100755100644
Lines changed: 135 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,43 +16,154 @@ namespace esphome
1616
private:
1717
static constexpr const char* TAG = "FanEntity";
1818
fan::Fan* fanPtr;
19+
20+
/**
21+
* @brief Handle incoming HomeKit write requests for a Fan service and apply them to the associated ESPHome fan.
22+
*
23+
* Processes writes for On, Rotation Speed, and Swing Mode characteristics, updates the HomeKit characteristic values and per-write statuses, batches any requested changes into a single fan call, and performs that call.
24+
*
25+
* @param write_data Array of write operations provided by HomeKit.
26+
* @param count Number of entries in `write_data`.
27+
* @param serv_priv Pointer to the associated fan::Fan instance (stored as service private data).
28+
* @param write_priv Unused write-private context supplied by HAP.
29+
* @return int HAP_SUCCESS.
30+
*/
1931
static int fanwrite(hap_write_data_t write_data[], int count, void* serv_priv, void* write_priv) {
2032
fan::Fan* fanPtr = (fan::Fan*)serv_priv;
2133
ESP_LOGD(TAG, "Write called for Accessory %s (%s)", std::to_string(fanPtr->get_object_id_hash()).c_str(), fanPtr->get_name().c_str());
2234
int i, ret = HAP_SUCCESS;
2335
hap_write_data_t* write;
36+
auto call = fanPtr->make_call();
37+
bool update_speed = false;
38+
bool update_state = false;
39+
bool update_oscillating = false;
40+
2441
for (i = 0; i < count; i++) {
2542
write = &write_data[i];
2643
if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_ON)) {
2744
ESP_LOGD(TAG, "Received Write for fan '%s' -> %s", fanPtr->get_name().c_str(), write->val.b ? "On" : "Off");
28-
ESP_LOGD(TAG, "[STATE] CURRENT STATE: %d", fanPtr->state);
29-
fanPtr->make_call().set_state(write->val.b).perform();
45+
call.set_state(write->val.b);
46+
update_state = true;
47+
hap_char_update_val(write->hc, &(write->val));
48+
*(write->status) = HAP_STATUS_SUCCESS;
49+
}
50+
else if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_ROTATION_SPEED)) {
51+
float speed_percentage = write->val.f;
52+
ESP_LOGD(TAG, "Received Write for fan '%s' speed -> %.1f%%", fanPtr->get_name().c_str(), speed_percentage);
53+
54+
// Direct mapping: HomeKit percentage (0-100) to ESPHome speed (0-100)
55+
int speed_level = static_cast<int>(speed_percentage);
56+
57+
ESP_LOGD(TAG, "Setting fan speed to level: %d", speed_level);
58+
call.set_speed(speed_level);
59+
update_speed = true;
60+
hap_char_update_val(write->hc, &(write->val));
61+
*(write->status) = HAP_STATUS_SUCCESS;
62+
}
63+
else if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_SWING_MODE)) {
64+
bool swing_mode = write->val.i;
65+
ESP_LOGD(TAG, "Received Write for fan '%s' oscillation -> %s", fanPtr->get_name().c_str(), swing_mode ? "On" : "Off");
66+
67+
call.set_oscillating(swing_mode);
68+
update_oscillating = true;
3069
hap_char_update_val(write->hc, &(write->val));
3170
*(write->status) = HAP_STATUS_SUCCESS;
3271
}
3372
else {
3473
*(write->status) = HAP_STATUS_RES_ABSENT;
3574
}
3675
}
76+
77+
if (update_state || update_speed || update_oscillating) {
78+
call.perform();
79+
}
3780
return ret;
3881
}
82+
83+
/**
84+
* @brief Reflects an ESPHome fan's current state into its HomeKit Fan service characteristics.
85+
*
86+
* Updates the accessory's On, Rotation Speed, and Swing Mode characteristics (if present)
87+
* to match the fan's current on/off state, speed (0–100 mapped directly to HomeKit percentage),
88+
* and oscillating state (1 = enabled, 0 = disabled). If the accessory, service, or any
89+
* characteristic is not found, the function simply returns without error.
90+
*
91+
* @param obj Pointer to the fan whose state will be propagated to HomeKit.
92+
*/
3993
static void on_fanupdate(fan::Fan* obj) {
40-
ESP_LOGD(TAG, "%s state: %s", obj->get_name().c_str(), ONOFF(obj->state));
94+
ESP_LOGD(TAG, "%s state: %s, speed: %d, oscillating: %s",
95+
obj->get_name().c_str(),
96+
ONOFF(obj->state),
97+
obj->speed,
98+
obj->oscillating ? "On" : "Off");
99+
41100
hap_acc_t* acc = hap_acc_get_by_aid(hap_get_unique_aid(std::to_string(obj->get_object_id_hash()).c_str()));
42101
if (acc) {
43102
hap_serv_t* hs = hap_acc_get_serv_by_uuid(acc, HAP_SERV_UUID_FAN);
44-
hap_char_t* on_char = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_ON);
45-
hap_val_t state;
46-
state.b = !!obj->state;
47-
hap_char_update_val(on_char, &state);
103+
if (hs) {
104+
// Update On characteristic
105+
hap_char_t* on_char = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_ON);
106+
if (on_char) {
107+
hap_val_t state;
108+
state.b = !!obj->state;
109+
hap_char_update_val(on_char, &state);
110+
}
111+
112+
// Update Rotation Speed characteristic
113+
hap_char_t* speed_char = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_ROTATION_SPEED);
114+
if (speed_char) {
115+
hap_val_t speed_val;
116+
// Direct mapping: ESPHome speed (0-100) to HomeKit percentage (0-100)
117+
speed_val.f = static_cast<float>(obj->speed);
118+
hap_char_update_val(speed_char, &speed_val);
119+
}
120+
121+
// Update Swing Mode characteristic
122+
hap_char_t* swing_char = hap_serv_get_char_by_uuid(hs, HAP_CHAR_UUID_SWING_MODE);
123+
if (swing_char) {
124+
hap_val_t swing_val;
125+
swing_val.i = obj->oscillating ? 1 : 0; // 1 = enabled, 0 = disabled
126+
hap_char_update_val(swing_char, &swing_val);
127+
}
128+
}
48129
}
49130
}
131+
132+
/**
133+
* @brief Handle an identify request for the accessory.
134+
*
135+
* Logs that the accessory was identified.
136+
*
137+
* @param ha Pointer to the HomeKit accessory being identified.
138+
* @return int `HAP_SUCCESS` on success.
139+
*/
50140
static int acc_identify(hap_acc_t* ha) {
51141
ESP_LOGI(TAG, "Accessory identified");
52142
return HAP_SUCCESS;
53143
}
144+
54145
public:
55-
FanEntity(fan::Fan* fanPtr) : HAPEntity({{MODEL, "HAP-FAN"}}), fanPtr(fanPtr) {}
146+
/**
147+
* @brief Construct a HomeKit Fan entity for an ESPHome fan.
148+
*
149+
* Initializes a HAP fan accessory wrapper using the model identifier "HAP-FAN"
150+
* and associates it with the provided ESPHome fan instance.
151+
*
152+
* @param fanPtr Pointer to the ESPHome `fan::Fan` instance to expose to HomeKit.
153+
* The pointer must remain valid for the lifetime of the FanEntity.
154+
*/
155+
FanEntity(fan::Fan* fanPtr) : HAPEntity({{MODEL, "HAP-FAN"}}), fanPtr(fanPtr) {}
156+
157+
/**
158+
* @brief Create and register a HomeKit accessory and Fan service for the ESPHome fan.
159+
*
160+
* Creates a HomeKit accessory using the accessory_info values (or sensible defaults),
161+
* exposes a Fan service with On, Rotation Speed, and Swing Mode characteristics
162+
* initialized from the underlying fan state, attaches a write callback so HomeKit
163+
* writes are applied to the fan, registers the accessory with the bridged HomeKit
164+
* database (unique AID derived from the fan object hash), and subscribes to fan
165+
* state updates so changes are reflected back into HomeKit.
166+
*/
56167
void setup() {
57168
hap_acc_cfg_t acc_cfg = {
58169
.model = strdup(accessory_info[MODEL]),
@@ -66,23 +177,35 @@ namespace esphome
66177
hap_acc_t* accessory = nullptr;
67178
hap_serv_t* service = nullptr;
68179
std::string accessory_name = fanPtr->get_name();
180+
69181
if (accessory_info[NAME] == NULL) {
70182
acc_cfg.name = strdup(accessory_name.c_str());
71183
}
72184
else {
73185
acc_cfg.name = strdup(accessory_info[NAME]);
74186
}
187+
75188
if (accessory_info[SN] == NULL) {
76189
acc_cfg.serial_num = strdup(std::to_string(fanPtr->get_object_id_hash()).c_str());
77190
}
78191
else {
79192
acc_cfg.serial_num = strdup(accessory_info[SN]);
80193
}
194+
81195
/* Create accessory object */
82196
accessory = hap_acc_create(&acc_cfg);
83-
/* Create the fan Service. */
197+
198+
/* Create the fan Service with initial state */
84199
service = hap_serv_fan_create(fanPtr->state);
85200

201+
// Add Rotation Speed characteristic
202+
hap_char_t* speed_char = hap_char_rotation_speed_create(static_cast<float>(fanPtr->speed));
203+
hap_serv_add_char(service, speed_char);
204+
205+
// Add Swing Mode characteristic
206+
hap_char_t* swing_char = hap_char_swing_mode_create(fanPtr->oscillating ? 1 : 0);
207+
hap_serv_add_char(service, swing_char);
208+
86209
ESP_LOGD(TAG, "ID HASH: %lu", fanPtr->get_object_id_hash());
87210
hap_serv_set_priv(service, fanPtr);
88211

@@ -94,9 +217,11 @@ namespace esphome
94217

95218
/* Add the Accessory to the HomeKit Database */
96219
hap_add_bridged_accessory(accessory, hap_get_unique_aid(std::to_string(fanPtr->get_object_id_hash()).c_str()));
220+
97221
if (!fanPtr->is_internal())
98222
fanPtr->add_on_state_callback([this]() { FanEntity::on_fanupdate(fanPtr); });
99-
ESP_LOGI(TAG, "Fan '%s' linked to HomeKit", accessory_name.c_str());
223+
224+
ESP_LOGI(TAG, "Fan '%s' linked to HomeKit with speed and oscillation control", accessory_name.c_str());
100225
}
101226
};
102227
}

0 commit comments

Comments
 (0)