Skip to content

Commit

Permalink
plugin: hass param parse update
Browse files Browse the repository at this point in the history
  • Loading branch information
xinhecuican committed Aug 9, 2024
1 parent 52e4df6 commit 0c345db
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
- name: test
run: |
mkdir build_test && cd build_test
cmake -DCMAKE_BUILD_TYPE=Debug -DTEST=ON ..
cmake -DCMAKE_BUILD_TYPE=Debug -DWAKEUP_DUILITE=OFF -DVAD_DUILITE=OFF -DWAKEUP_SNOWBOY=ON -DVAD_SILERO=ON -DNETEASE_USE_JS=ON -DTEST=ON ..
make -j8
cd Debug/bin
./QSmartAssistant asrSherpa hassRegex
11 changes: 8 additions & 3 deletions Data/default_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,16 @@
},
{
"intent": "CLOSE_FURNITURE",
"slotName": "furniture",
"slotValue": "热水器",
"slots": [
{
"slotName": "furniture",
"slotValue": "热水器"
}
],
"path": "/api/services/switch/turn_off",
"params": {
"entity_id": "switch.cuco_cp2d_46fc_switch"
"entity_id": "switch.cuco_cp2d_46fc_switch",
"time": "{number|--}"
}
}
]
Expand Down
2 changes: 2 additions & 0 deletions Data/rasa/data/jieba_userdict/dict.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
6 changes: 6 additions & 0 deletions Data/rasa/data/nlu/nlu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ nlu:
- 打开[卧室]{"entity": "location","role": "home"}的[灯](furniture)
- 打开[客厅]{"entity": "location","role": "home"}的[风扇](furniture)
- 打开[扫地机器人](furniture)
- [热水器](furniture)开[20](number)分钟
- intent: CLOSE_FURNITURE
examples: |
- 关闭[空调](furniture)
Expand All @@ -120,6 +121,11 @@ nlu:
- 关闭[卧室]{"entity": "location","role": "home"}的[灯](furniture)
- 关闭[客厅]{"entity": "location","role": "home"}的[风扇](furniture)
- 关闭[扫地机器人](furniture)
- intent: FURNITURE_INFO
examples: |
- [卧室]{"entity": "location","role": "home"}的温度
- [灯](furniture)是否开启
- [客厅]{"entity": "location","role": "home"}是否有人
- intent: SYS_INFO
examples: |
- 当前系统信息
Expand Down
1 change: 1 addition & 0 deletions Data/rasa/domain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ intents:
- PAUSE
- OPEN_FURNITURE
- CLOSE_FURNITURE
- FURNITURE_INFO
- SYS_INFO
- Weather
- CHAT
Expand Down
Binary file modified Data/rasa/models/default.tar.gz
Binary file not shown.
109 changes: 74 additions & 35 deletions Plugins/hass/hass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,23 @@ bool Hass::handle(const QString &text, const ParsedIntent &parsedIntent,
if (service.pattern != "") {
if (text.contains(QRegExp(service.pattern))) {
QJsonObject params = parseParams(intent, service);
executeService(service.path, params);
executeService(service.path, params, service.notify);
return true;
}
} else {
bool success;
IntentSlot slot = intent.getSlot(service.slotName, success);
if (slot.value == service.slotValue) {
bool needExecute = true;
for (int i = 0; i < service.slotName.size(); i++) {
IntentSlot slot =
intent.getSlot(service.slotName.at(i), success);
if (slot.value != service.slotValue.at(i) || !success) {
needExecute = false;
continue;
}
}
if (needExecute) {
QJsonObject params = parseParams(intent, service);
executeService(service.path, params);
executeService(service.path, params, service.notify);
return true;
}
}
Expand All @@ -45,8 +53,20 @@ void Hass::setPluginHelper(IPluginHelper *helper) {
hassService.pattern = service.value("pattern").toString();
hassService.path = service.value("path").toString();
hassService.params = service.value("params").toObject();
hassService.slotName = service.value("slotName").toString();
hassService.slotValue = service.value("slotValue").toString();
if (service.contains("slotName") && service.contains("slotValue")) {
hassService.slotName.append(service.value("slotName").toString());
hassService.slotValue.append(service.value("slotValue").toString());
}
if (service.contains("slots")) {
QJsonArray _slots = service.value("slots").toArray();
for (auto iter2 = _slots.begin(); iter2 != _slots.end(); iter2++) {
QJsonObject slot = iter2->toObject();
hassService.slotName.append(slot.value("slotName").toString());
hassService.slotValue.append(
slot.value("slotValue").toString());
}
}
hassService.notify = service.value("notify").toBool();
QString intent = service.value("intent").toString();
if (!this->services.contains(intent)) {
QList<HassService> hassServices;
Expand All @@ -63,49 +83,68 @@ void Hass::recvMessage(const QString &text, const ParsedIntent &parsedIntent,

QJsonObject Hass::parseParams(const Intent &intent,
const HassService &service) {
QJsonObject params;
QRegExp keyFinder("\\{(.*)\\}");
QJsonObject params = parseObject(intent, service.params);
intent.toString(0);
qDebug() << params;
return params;
}

QJsonObject Hass::parseObject(const Intent &intent, const QJsonObject &object) {
QJsonObject result;
for (auto iter = object.begin(); iter != object.end(); iter++) {
if (iter.value().isObject()) {
result.insert(iter.key(),
parseObject(intent, iter.value().toObject()));
} else {
QString value = parseValue(intent, iter.value());
if (value != "")
result.insert(iter.key(), value);
}
}
return result;
}

QString Hass::parseValue(const Intent &intent, const QJsonValue &v) {
QRegExp keyFinder("\\{(.*)\\|(.*)\\}");
keyFinder.setMinimal(true);
for (auto iter = service.params.begin(); iter != service.params.end();
iter++) {
QString value = iter->toString();
QString result = "";
int pos = 0;
int lastPos = 0;
while ((pos = keyFinder.indexIn(value, pos)) != -1) {
pos += keyFinder.matchedLength();
QString key = keyFinder.cap(1);
bool success = false;
IntentSlot slot = intent.getSlot(key, success);
if (success) {
result += value.midRef(lastPos, keyFinder.pos(1) - lastPos - 1);
result += slot.value;
lastPos = pos;
} else {
qWarning() << "hass params unfind" << key;
result += value.midRef(lastPos, keyFinder.pos(1) - lastPos - 1);
lastPos = pos;
QString value = v.toString();
QString result = "";
int pos = 0;
int lastPos = 0;
while ((pos = keyFinder.indexIn(value, pos)) != -1) {
pos += keyFinder.matchedLength();
QString key = keyFinder.cap(1);
bool success = false;
IntentSlot slot = intent.getSlot(key, success);
if (success) {
result += value.midRef(lastPos, keyFinder.pos(1) - lastPos - 1);
result += slot.value;
lastPos = pos;
} else {
QString defaultValue = keyFinder.cap(2);
if (defaultValue != "--") {
result += defaultValue;
}
lastPos = pos;
}
if (lastPos < value.size()) {
result += value.midRef(lastPos);
}
params.insert(iter.key(), result);
}
return params;
if (lastPos < value.size()) {
result += value.midRef(lastPos);
}
return result;
}

void Hass::executeService(const QString &path, const QJsonObject &params) {
void Hass::executeService(const QString &path, const QJsonObject &params,
bool notify) {
request.setUrl(QUrl(urlPrefix + path));
QJsonDocument doc(params);
QNetworkReply *reply = manager.post(request, doc.toJson());
connect(reply, &QNetworkReply::finished, this, [=]() {
if (reply->error() != QNetworkReply::NoError) {
qWarning() << "requst fail" << reply->error();
} else {
} else if (notify) {
helper->say("执行成功");
}
// qDebug() << reply->readAll();
reply->deleteLater();
});
}
9 changes: 6 additions & 3 deletions Plugins/hass/hass.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@ class Hass : public QObject, Plugin
void sendMessage(PluginMessage message) override;
private:
struct HassService{
bool notify = false;
QString pattern;
QString path;
QJsonObject params;
QString slotName;
QString slotValue;
QList<QString> slotName;
QList<QString> slotValue;
};
private:
void executeService(const QString& path, const QJsonObject& params);
void executeService(const QString& path, const QJsonObject& params, bool notify);
QJsonObject parseParams(const Intent& intent, const HassService& service);
QJsonObject parseObject(const Intent& intent, const QJsonObject& object);
QString parseValue(const Intent& intent, const QJsonValue& value);
private:
QMap<QString, QList<HassService>> services;
QString urlPrefix;
Expand Down
5 changes: 4 additions & 1 deletion Plugins/systeminfo/systeminfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ void SystemInfo::recvMessage(const QString &text,

bool SystemInfo::handle(const QString &text, const ParsedIntent &parsedIntent,
bool &isImmersive) {
Q_UNUSED(text)
Q_UNUSED(isImmersive)
if (text == "重新加载设置") {
helper->getConfig()->loadConfig();
return true;
}
for (auto &intent : parsedIntent.intents) {
if (intent.name == "SYS_INFO") {
QFile temperature("/sys/class/thermal/thermal_zone0/temp");
Expand Down
30 changes: 30 additions & 0 deletions Test/tst_sherpa.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,35 @@ private slots:
qDebug() << result;
QCOMPARE(result, "test增大有多少30");
}

void hassParam() {
QString pluginName = "Hass";
QPluginLoader loader(QDir::homePath() +
"/.config/QSmartAssistant/plugins/lib" +
pluginName + ".so");
QObject *plugin = loader.instance();
if (plugin) {
auto info = qobject_cast<Plugin *>(plugin);
if (info) {
connect(plugin, SIGNAL(sendMessage(PluginMessage)), this,
SLOT(handleMessage(PluginMessage)));
info->setPluginHelper(new TestPluginHelper);
qInfo() << "load plugin" << pluginName << "success";
ParsedIntent parsedIntent;
Intent intent;
intent.name = "OPEN_FURNITURE";
intent.appendSlot(IntentSlot("furniture", "热水器", 0));
intent.appendSlot(IntentSlot("number", "20", 0));
parsedIntent.append(intent);
bool immersive = false;
info->handle("热水器开20分钟", parsedIntent, immersive);
} else {
qInfo() << "load Plugin" << pluginName << "cast fail";
}
} else {
qInfo() << "load plugin" << pluginName << "fail";
}
QTest::qWait(3000);
}
};
#endif // TST_SHERPA_H
46 changes: 39 additions & 7 deletions doc/插件.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,39 @@ services配置方式:
"services": [
{
"intent": "OPEN_FURNITURE",
"pattern": ".*热水器.*",
notify: true,
"slots": [
{
"slotName": "furniture",
"slotValue": "热水器"
}
]
"path": "/api/services/switch/turn_on",
"params": {
"entity_id": "switch.cuco_cp2d_46fc_switch"
}
},
{
"intent": "CLOSE_FURNITURE",
"slotName": "furniture",
"slotValue": "热水器",
"path": "/api/services/switch/turn_off",
"intent": "OPEN_FURNITURE",
"notify": true,
"params": {
"entity_id": "switch.cuco_cp2d_46fc_switch"
}
"entity_id": "script.cuco_cp2d_46fc_switch_timer",
"variables": {
"duration": "{time|--}"
}
},
"path": "/api/services/script/turn_on",
"pattern": ".*热水器.*"
}
]
当遇到intent时开始进行匹配
匹配可以根据nlu提取出来的slotName和slotValue进行匹配,也可以使用正则表达式pattern进行匹配
path是调用api的路径,具体可以参考https://developers.home-assistant.io/docs/api/rest/
params是传递给homeassistant的参数,注意其中可以使用{slotName}引用slot的内容
notify: 执行完成之后是否提示执行成功

参数可以按照{slotName|defaultValue}的样式使用槽中的值,
如果defaultValue为--则匹配失败时不添加该参数
```
调用样例:
Expand All @@ -102,6 +115,25 @@ params是传递给homeassistant的参数,注意其中可以使用{slotName}引
- 光灯
- 打开热水器
- 关闭热水器
- 热水器开20分钟
homeassistant中脚本编写样例:
```
alias: 定时热水器
sequence:
- type: turn_on
device_id: 23403927ea9d0ff1c17a44871a1c75ee
entity_id: 6afa4b81894ad5ac9e212a9b742150e6
domain: switch
- condition: template
value_template: "{{duration is defined }}"
- delay: "{{ duration }}"
- type: turn_off
device_id: 23403927ea9d0ff1c17a44871a1c75ee
entity_id: 6afa4b81894ad5ac9e212a9b742150e6
domain: switch
description: ""
```
## netease music
Expand Down

0 comments on commit 0c345db

Please sign in to comment.