Skip to content

Commit

Permalink
report battery level in Home from a smart lock
Browse files Browse the repository at this point in the history
  • Loading branch information
rednblkx committed Nov 17, 2024
1 parent 3cc63d6 commit c5c8533
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 7 deletions.
18 changes: 14 additions & 4 deletions data/routes/misc.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,16 @@ <h3 style="margin: 0;width: fit-content;padding: 0.5rem;cursor: pointer;" onclic
<span style="height: 1px;border-top: 1px #424242 solid;display: block;margin: 0;padding: 0;"></span>
</div>
<div class="custom-tabs-selected-body" data-custom-tabs-body="0">
<div style="display: flex;flex-direction: column;">
<div style="display: flex;gap: 8px;">
<label for="device-name">Device Name</label>
<input type="text" name="device-name" id="device-name" placeholder="HK" required value="%DEVICENAME%"
style="width: fit-content;" />
</div>

<div style="display: flex;flex-direction: column;">
<div style="display: flex;gap: 8px;">
<label for="hk-setupcode">Setup Code</label>
<input type="number" name="hk-setupcode" id="hk-setupcode" placeholder="46637726" required
value="%HKSETUPCODE%" style="width: fit-content;" />
</div>

<div style="display: flex;gap: 8px;">
<label for="hk-always-lock">Always Lock on HomeKey</label>
<select name="hk-always-lock" id="hk-always-lock">
Expand All @@ -40,6 +38,17 @@ <h3 style="margin: 0;width: fit-content;padding: 0.5rem;cursor: pointer;" onclic
<option value="1">Enabled</option>
</select>
</div>
<div style="display: flex;gap: 8px;">
<label for="prox-bat-enable">SmartLock battery reporting</label>
<select name="prox-bat-enable" id="prox-bat-enable">
<option value="0">Disabled</option>
<option value="1">Enabled</option>
</select>
</div>
<div style="display: flex;gap: 8px;">
<label for="btr-low-threshold">Battery low status Threshold</label>
<input type="number" name="btr-low-threshold" id="btr-low-threshold" placeholder="10" min="0" max="100" value="%BTRLOWTHRESHOLD%" style="width: 4rem;" />
</div>
<fieldset>
<legend>HomeKey Card Finish:</legend>
<div style="display: flex;justify-content: space-evenly;margin-bottom: 0;padding-bottom: 0;">
Expand Down Expand Up @@ -170,6 +179,7 @@ <h3 class="webui-tabs-selected-tab" style="margin: 0;width: fit-content;padding:
document.getElementById("hk-always-unlock").selectedIndex = "%ALWAYSUNLOCK%";
document.getElementById("hk-always-lock").selectedIndex = "%ALWAYSLOCK%";
document.getElementById("web-auth-enable").selectedIndex = "%WEBENABLE%";
document.getElementById("prox-bat-enable").selectedIndex = "%PROXBATENABLE%";
let form = document.getElementById("misc-config");
async function handleForm(event) {
event.preventDefault();
Expand Down
4 changes: 4 additions & 0 deletions data/routes/mqtt.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ <h3 style="text-align: center;margin-top: 0;">MQTT Topics</h3>
<label for="mqtt-tstatecmd">Lock Target State Cmd Topic</label>
<input type="text" name="mqtt-tstatecmd" id="mqtt-tstatecmd" placeholder="topic/set_target_state" required value="%TSTATECMD%">
</div>
<div style="display: flex; flex-direction: column;">
<label for="mqtt-btrprox-cmd-topic">SmartLock battery level Cmd Topic</label>
<input type="text" name="mqtt-btrprox-cmd-topic" id="mqtt-btrprox-cmd-topic" placeholder="topic/set_battery_level" required value="%BTRLEVELCMD%">
</div>
</div>
</div>
<div class="mqtt-topics-hidden-body" data-mqtt-topics-body="1">
Expand Down
1 change: 1 addition & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ enum class gpioMomentaryStateStatus : uint8_t
#define MQTT_SET_TARGET_STATE_TOPIC "homekit/set_target_state" // MQTT Control Topic for the HomeKit lock target state
#define MQTT_SET_CURRENT_STATE_TOPIC "homekit/set_current_state" // MQTT Control Topic for the HomeKit lock current state
#define MQTT_STATE_TOPIC "homekit/state" // MQTT Topic for publishing the HomeKit lock target state
#define MQTT_PROX_BAT_TOPIC "homekit/set_battery_lvl" // MQTT Topic for publishing the HomeKit lock target state

// Miscellaneous
#define HOMEKEY_COLOR TAN
Expand Down
64 changes: 61 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ namespace espConfig
lockTStateCmd.append(id).append("/" MQTT_SET_TARGET_STATE_TOPIC);
lockCustomStateTopic.append(id).append("/" MQTT_CUSTOM_STATE_TOPIC);
lockCustomStateCmd.append(id).append("/" MQTT_CUSTOM_STATE_CTRL_TOPIC);
btrLvlCmdTopic.append(id).append("/" MQTT_PROX_BAT_TOPIC);
}
/* MQTT Broker */
std::string mqttBroker = MQTT_HOST;
Expand All @@ -85,6 +86,7 @@ namespace espConfig
std::string lockStateCmd;
std::string lockCStateCmd;
std::string lockTStateCmd;
std::string btrLvlCmdTopic;
/* MQTT Custom State */
std::string lockCustomStateTopic;
std::string lockCustomStateCmd;
Expand All @@ -94,7 +96,7 @@ namespace espConfig
bool nfcTagNoPublish = false;
std::map<std::string, int> customLockStates = { {"C_LOCKED", C_LOCKED}, {"C_UNLOCKING", C_UNLOCKING}, {"C_UNLOCKED", C_UNLOCKED}, {"C_LOCKING", C_LOCKING}, {"C_JAMMED", C_JAMMED}, {"C_UNKNOWN", C_UNKNOWN} };
std::map<std::string, int> customLockActions = { {"UNLOCK", UNLOCK}, {"LOCK", LOCK} };
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(espConfig::mqttConfig_t, mqttBroker, mqttPort, mqttUsername, mqttPassword, mqttClientId, lwtTopic, hkTopic, lockStateTopic, lockStateCmd, lockCStateCmd, lockTStateCmd, lockCustomStateTopic, lockCustomStateCmd, lockEnableCustomState, hassMqttDiscoveryEnabled, customLockStates, customLockActions, nfcTagNoPublish)
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(espConfig::mqttConfig_t, mqttBroker, mqttPort, mqttUsername, mqttPassword, mqttClientId, lwtTopic, hkTopic, lockStateTopic, lockStateCmd, lockCStateCmd, lockTStateCmd, lockCustomStateTopic, lockCustomStateCmd, lockEnableCustomState, hassMqttDiscoveryEnabled, customLockStates, customLockActions, nfcTagNoPublish, btrLvlCmdTopic)
} mqttData;

struct misc_config_t
Expand Down Expand Up @@ -135,13 +137,17 @@ namespace espConfig
std::string webUsername = WEB_AUTH_USERNAME;
std::string webPassword = WEB_AUTH_PASSWORD;
std::array<uint8_t, 4> nfcGpioPins{SS, SCK, MISO, MOSI};
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(misc_config_t, deviceName, otaPasswd, hk_key_color, setupCode, lockAlwaysUnlock, lockAlwaysLock, controlPin, hsStatusPin, nfcSuccessPin, nfcSuccessTime, nfcNeopixelPin, neopixelSuccessColor, neopixelFailureColor, neopixelSuccessTime, neopixelFailTime, nfcSuccessHL, nfcFailPin, nfcFailTime, nfcFailHL, gpioActionPin, gpioActionLockState, gpioActionUnlockState, gpioActionMomentaryEnabled, gpioActionMomentaryTimeout, webAuthEnabled, webUsername, webPassword, nfcGpioPins)
uint8_t btrLowStatusThreshold = 10;
bool proxBatEnabled = false;
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(misc_config_t, deviceName, otaPasswd, hk_key_color, setupCode, lockAlwaysUnlock, lockAlwaysLock, controlPin, hsStatusPin, nfcSuccessPin, nfcSuccessTime, nfcNeopixelPin, neopixelSuccessColor, neopixelFailureColor, neopixelSuccessTime, neopixelFailTime, nfcSuccessHL, nfcFailPin, nfcFailTime, nfcFailHL, gpioActionPin, gpioActionLockState, gpioActionUnlockState, gpioActionMomentaryEnabled, gpioActionMomentaryTimeout, webAuthEnabled, webUsername, webPassword, nfcGpioPins, btrLowStatusThreshold, proxBatEnabled)
} miscConfig;
};

KeyFlow hkFlow = KeyFlow::kFlowFAST;
SpanCharacteristic* lockCurrentState;
SpanCharacteristic* lockTargetState;
SpanCharacteristic* statusLowBtr;
SpanCharacteristic* btrLevel;
esp_mqtt_client_handle_t client = nullptr;

std::unique_ptr<Pixel> pixel;
Expand All @@ -155,6 +161,15 @@ bool save_to_nvs() {
return !set_nvs && !commit_nvs;
}

struct PhysicalLockBattery : Service::BatteryService
{
PhysicalLockBattery() {
LOG(D, "Configuring PhysicalLockBattery");
statusLowBtr = new Characteristic::StatusLowBattery(0, true);
btrLevel = new Characteristic::BatteryLevel(100, true);
}
};

struct LockManagement : Service::LockManagement
{
SpanCharacteristic* lockControlPoint;
Expand Down Expand Up @@ -682,6 +697,9 @@ void mqtt_connected_event(void* event_handler_arg, esp_event_base_t event_base,
if (espConfig::mqttData.lockEnableCustomState) {
esp_mqtt_client_subscribe(client, espConfig::mqttData.lockCustomStateCmd.c_str(), 0);
}
if (espConfig::miscConfig.proxBatEnabled) {
esp_mqtt_client_subscribe(client, espConfig::mqttData.btrLvlCmdTopic.c_str(), 0);
}
esp_mqtt_client_subscribe(client, espConfig::mqttData.lockStateCmd.c_str(), 0);
esp_mqtt_client_subscribe(client, espConfig::mqttData.lockCStateCmd.c_str(), 0);
esp_mqtt_client_subscribe(client, espConfig::mqttData.lockTStateCmd.c_str(), 0);
Expand Down Expand Up @@ -709,6 +727,13 @@ void mqtt_data_handler(void* event_handler_arg, esp_event_base_t event_base, int
lockCurrentState->setVal(state);
esp_mqtt_client_publish(client, espConfig::mqttData.lockStateTopic.c_str(), std::to_string(lockCurrentState->getVal()).c_str(), 0, 1, true);
}
} else if (!strcmp(espConfig::mqttData.btrLvlCmdTopic.c_str(), topic.c_str())) {
btrLevel->setVal(state);
if (state <= espConfig::miscConfig.btrLowStatusThreshold) {
statusLowBtr->setVal(1);
} else {
statusLowBtr->setVal(0);
}
}
}

Expand Down Expand Up @@ -800,6 +825,10 @@ String miscHtmlProcess(const String& var) {
return String(espConfig::miscConfig.nfcGpioPins[2]);
} else if (var == "NFCMOSIGPIOPIN") {
return String(espConfig::miscConfig.nfcGpioPins[3]);
} else if (var == "BTRLOWTHRESHOLD") {
return String(espConfig::miscConfig.btrLowStatusThreshold);
} else if (var == "PROXBATENABLE") {
return String(espConfig::miscConfig.proxBatEnabled);
}
return String();
}
Expand Down Expand Up @@ -883,6 +912,8 @@ String mqttHtmlProcess(const String& var) {
return String(espConfig::mqttData.customLockStates["C_UNKNOWN"]);
} else if (var == "NFCTAGSNOPUBLISH") {
return String(espConfig::mqttData.nfcTagNoPublish);
} else if (var == "BTRLEVELCMD") {
return String(espConfig::mqttData.btrLvlCmdTopic.c_str());
}
return "";
}
Expand Down Expand Up @@ -1122,6 +1153,17 @@ void setupWeb() {
return;
}
espConfig::miscConfig.nfcGpioPins[3] = p->value().toInt();
} else if (!strcmp(p->name().c_str(), "prox-bat-enable")) {
espConfig::miscConfig.proxBatEnabled = p->value().toInt();
} else if (!strcmp(p->name().c_str(), "btr-low-threshold")) {
espConfig::miscConfig.btrLowStatusThreshold = p->value().toInt();
if (statusLowBtr && btrLevel) {
if (btrLevel->getVal() <= espConfig::miscConfig.btrLowStatusThreshold) {
statusLowBtr->setVal(1);
} else {
statusLowBtr->setVal(0);
}
}
}
}
json json_misc_config = espConfig::miscConfig;
Expand Down Expand Up @@ -1614,7 +1656,20 @@ void setup() {
}
save_to_nvs();
});

new SpanUserCommand('N', "Btr status low", [](const char* arg) {
const char* TAG = "BTR_LOW";
if (strncmp(arg + 1, "0", 1) == 0) {
statusLowBtr->setVal(0);
LOG(I, "Low status set to NORMAL");
} else if (strncmp(arg + 1, "1", 1) == 0) {
statusLowBtr->setVal(1);
LOG(I, "Low status set to LOW");
}
});
new SpanUserCommand('B', "Btr level", [](const char* arg) {
uint8_t level = atoi(static_cast<const char *>(arg + 1));
btrLevel->setVal(level);
});

new SpanAccessory();
new Service::AccessoryInformation();
Expand All @@ -1640,6 +1695,9 @@ void setup() {
new NFCAccess();
new Service::HAPProtocolInformation();
new Characteristic::Version();
if (espConfig::miscConfig.proxBatEnabled) {
new PhysicalLockBattery();
}
homeSpan.setControllerCallback(pairCallback);
homeSpan.setWifiCallback(wifiCallback);
if (espConfig::miscConfig.nfcNeopixelPin != 255) {
Expand Down

0 comments on commit c5c8533

Please sign in to comment.