mirror of
https://github.com/chliny/hass-tencentcloud-asr.git
synced 2026-07-21 22:53:58 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a05335b3c | ||
|
|
667a735fe4 | ||
|
|
40b15e6e9a | ||
|
|
f1cd7fff72 | ||
|
|
17fb066906 |
@@ -1,25 +1,32 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release-zip:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: ZIP Component Dir
|
||||
run: |
|
||||
cd ${{ github.workspace }}/custom_components/tencentcloud_asr
|
||||
zip -r tencentcloud_asr.zip ./
|
||||
zip -r ${{ github.workspace }}/tencentcloud_asr.zip ./
|
||||
|
||||
- name: Upload zip to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ github.workspace }}/custom_components/tencentcloud_asr/tencentcloud_asr.zip
|
||||
asset_name: tencentcloud_asr.zip
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
tag_name: ${{ github.ref_name }}
|
||||
name: Release ${{ github.ref_name }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
files: tencentcloud_asr.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
腾讯云语音识别服务 homeassistant stt集成
|
||||
|
||||
[![GitHub release][releases-shield]][releases]
|
||||
[![Github downloads][downloads-shield]][releases]
|
||||
[![HACS][hacs-shield]][hacs]
|
||||
|
||||
## 安装
|
||||
|
||||
- 腾讯云上开通[语音识别服务](https://cloud.tencent.com/product/asr),获取SecretId 和 SecretKey
|
||||
@@ -18,12 +22,36 @@
|
||||
|
||||
| 模型 | 说明 |
|
||||
|------|------|
|
||||
|8k_zh|中文电话通用|
|
||||
|8k_en|英文电话通用|
|
||||
|16k_zh| 中文通用|
|
||||
|16k_zh-PY|中英粤|
|
||||
|16k_zh_medical|中文医疗|
|
||||
|16k_en|英语|
|
||||
|16k_yue|粤语|
|
||||
|16k_ja|日语|
|
||||
|16k_ko|韩语|
|
||||
|16k_vi|越南语|
|
||||
|16k_ms|马来语|
|
||||
|16k_id|印度尼西亚语|
|
||||
|16k_fil|菲律宾语|
|
||||
|16k_th|泰语|
|
||||
|16k_pt|葡萄牙语|
|
||||
|16k_tr|土耳其语|
|
||||
|16k_ar|阿拉伯语|
|
||||
|16k_es|西班牙语|
|
||||
|16k_hi|印地语|
|
||||
|16k_fr|法语|
|
||||
|16k_de|德语|
|
||||
|16k_zh_dialect|多方言,支持23种方言(上海话、四川话、武汉话、贵阳话、昆明话、西安话、郑州话、太原话、兰州话、银川话、西宁话、南京话、合肥话、南昌话、长沙话、苏州话、杭州话、济南话、天津话、石家庄话、黑龙江话、吉林话、辽宁话)|
|
||||
|
||||
- 更多模型详细说明,请参考[腾讯云文档](https://cloud.tencent.com/document/product/1093/35646)
|
||||
- 默认模型: `16k_zh`
|
||||
|
||||
|
||||
|
||||
[releases-shield]: https://img.shields.io/github/v/release/chliny/hass-tencentcloud-asr?style=flat-square
|
||||
[downloads-shield]: https://img.shields.io/github/downloads/chliny/hass-tencentcloud-asr/total?style=flat-square
|
||||
[releases]: https://github.com/chliny/hass-tencentcloud-asr/releases
|
||||
[hacs-shield]: https://img.shields.io/badge/HACS-Default-41BDF5.svg?style=flat-square
|
||||
[hacs]: https://github.com/hacs/integration
|
||||
|
||||
@@ -13,6 +13,10 @@ _LOGGER = logging.getLogger(__name__)
|
||||
SecretIdKey = "secretid"
|
||||
SecretKeyKey = "secretkey"
|
||||
ModelKey = "model"
|
||||
FilterDirtyKey = "filter_dirty"
|
||||
FilterModalKey = "filter_modal"
|
||||
FilterPuncKey = "filter_punc"
|
||||
ConvertNumModeKey = "convert_num_mode"
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
|
||||
|
||||
@@ -3,11 +3,24 @@ import voluptuous as vol
|
||||
from typing import Any, Dict
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow, ConfigEntry
|
||||
from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig, SelectOptionDict
|
||||
from homeassistant.helpers.selector import (
|
||||
BooleanSelector,
|
||||
SelectOptionDict,
|
||||
SelectSelector,
|
||||
SelectSelectorConfig,
|
||||
)
|
||||
|
||||
from .tencentcloud_api import ModuleSupportLanguage, DefaultModel
|
||||
from .tencentcloud_api import TencentCloudAsrAPi
|
||||
from . import SecretIdKey, SecretKeyKey, ModelKey
|
||||
from . import (
|
||||
ConvertNumModeKey,
|
||||
FilterDirtyKey,
|
||||
FilterModalKey,
|
||||
FilterPuncKey,
|
||||
ModelKey,
|
||||
SecretIdKey,
|
||||
SecretKeyKey,
|
||||
)
|
||||
from . import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -22,6 +35,17 @@ modelSelector = SelectSelector(
|
||||
)
|
||||
)
|
||||
|
||||
filterDirtySelector = SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=[
|
||||
SelectOptionDict(value="0", label="0"),
|
||||
SelectOptionDict(value="1", label="1"),
|
||||
SelectOptionDict(value="2", label="2"),
|
||||
],
|
||||
translation_key="filter_dirty_descriptions",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
async def async_step_user(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
|
||||
@@ -43,6 +67,10 @@ class ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
vol.Required(SecretIdKey): str,
|
||||
vol.Required(SecretKeyKey): str,
|
||||
vol.Required(ModelKey, default=DefaultModel): modelSelector,
|
||||
vol.Required(FilterDirtyKey, default="0"): filterDirtySelector,
|
||||
vol.Required(FilterModalKey, default=False): BooleanSelector(),
|
||||
vol.Required(FilterPuncKey, default=False): BooleanSelector(),
|
||||
vol.Required(ConvertNumModeKey, default=True): BooleanSelector(),
|
||||
})
|
||||
|
||||
return self.async_show_form(step_id="user", data_schema=config_schema, errors=errors)
|
||||
@@ -68,5 +96,21 @@ class TencentCloudAsrOptionFlow(OptionsFlow):
|
||||
model = user_input.get(ModelKey, DefaultModel)
|
||||
config_schema = vol.Schema({
|
||||
vol.Optional(ModelKey, default=model): modelSelector,
|
||||
vol.Required(
|
||||
FilterDirtyKey,
|
||||
default=user_input.get(FilterDirtyKey, "0"),
|
||||
): filterDirtySelector,
|
||||
vol.Required(
|
||||
FilterModalKey,
|
||||
default=user_input.get(FilterModalKey, False),
|
||||
): BooleanSelector(),
|
||||
vol.Required(
|
||||
FilterPuncKey,
|
||||
default=user_input.get(FilterPuncKey, False),
|
||||
): BooleanSelector(),
|
||||
vol.Required(
|
||||
ConvertNumModeKey,
|
||||
default=user_input.get(ConvertNumModeKey, True),
|
||||
): BooleanSelector(),
|
||||
})
|
||||
return self.async_show_form(step_id="user", data_schema=config_schema, errors={})
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
"loggers": [
|
||||
"tencentcloud_asr"
|
||||
],
|
||||
"version": "0.0.1"
|
||||
"version": "0.1.1"
|
||||
}
|
||||
|
||||
@@ -10,7 +10,15 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from .tencentcloud_api import TencentCloudAsrAPi
|
||||
from .tencentcloud_api import ModuleSupportLanguage
|
||||
from .tencentcloud_api import DefaultModel
|
||||
from . import SecretIdKey, SecretKeyKey, ModelKey
|
||||
from . import (
|
||||
ConvertNumModeKey,
|
||||
FilterDirtyKey,
|
||||
FilterModalKey,
|
||||
FilterPuncKey,
|
||||
ModelKey,
|
||||
SecretIdKey,
|
||||
SecretKeyKey,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -32,6 +40,7 @@ class ASRSTT(stt.SpeechToTextEntity):
|
||||
self._attr_unique_id = f"tencentcloud_asr_stt"
|
||||
self._attr_name = f"TencentCloud Asr STT"
|
||||
self.hass = hass
|
||||
self.config_entry = config_entry
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
@@ -52,6 +61,9 @@ class ASRSTT(stt.SpeechToTextEntity):
|
||||
|
||||
@property
|
||||
def supported_sample_rates(self) -> list[stt.AudioSampleRates]:
|
||||
model: str = self.hass.data.get(ModelKey, DefaultModel)
|
||||
if model.startswith("8k_"):
|
||||
return [stt.AudioSampleRates.SAMPLERATE_8000]
|
||||
return [stt.AudioSampleRates.SAMPLERATE_16000]
|
||||
|
||||
@property
|
||||
@@ -68,9 +80,25 @@ class ASRSTT(stt.SpeechToTextEntity):
|
||||
audio += chunk
|
||||
|
||||
data = base64.b64encode(audio).decode("utf8", "ignore").strip()
|
||||
model: str = self.hass.data.get(ModelKey, DefaultModel)
|
||||
_LOGGER.debug(f"process_audio_stream transcribe: {data} {model}")
|
||||
ret, result = await self.hass.async_add_executor_job(partial(self.tencentCloudApi.SentenceRecognition, model, data))
|
||||
config = {**self.config_entry.data, **self.config_entry.options}
|
||||
model: str = config.get(ModelKey, DefaultModel)
|
||||
_LOGGER.debug(
|
||||
"process_audio_stream transcribe: audio_bytes=%s model=%s",
|
||||
len(audio),
|
||||
model,
|
||||
)
|
||||
ret, result = await self.hass.async_add_executor_job(
|
||||
partial(
|
||||
self.tencentCloudApi.SentenceRecognition,
|
||||
model,
|
||||
data,
|
||||
len(audio),
|
||||
config.get(FilterDirtyKey, "0"),
|
||||
config.get(FilterModalKey, False),
|
||||
config.get(FilterPuncKey, False),
|
||||
config.get(ConvertNumModeKey, True),
|
||||
)
|
||||
)
|
||||
if not ret:
|
||||
return stt.SpeechResult(result, stt.SpeechResultState.ERROR)
|
||||
if not result:
|
||||
|
||||
@@ -11,10 +11,27 @@ _LOGGER = logging.getLogger(__name__)
|
||||
SentenceRecognitionSourceTypePost = 1
|
||||
SentenceRecognitionSourceTypeURL = 0
|
||||
ModuleSupportLanguage = {
|
||||
"8k_zh": ["zh-cn"],
|
||||
"8k_en": ["en"],
|
||||
"16k_zh": ["zh-cn"],
|
||||
"16k_zh-PY": ["zh-cn", "en", "zh-hk"],
|
||||
"16k_zh_medical": ["zh-cn"],
|
||||
"16k_en": ["en"],
|
||||
"16k_yue": ["zh-hk"],
|
||||
"16k_ja": ["ja"],
|
||||
"16k_ko": ["ko"],
|
||||
"16k_vi": ["vi"],
|
||||
"16k_ms": ["ms"],
|
||||
"16k_id": ["id"],
|
||||
"16k_fil": ["fil"],
|
||||
"16k_th": ["th"],
|
||||
"16k_pt": ["pt"],
|
||||
"16k_tr": ["tr"],
|
||||
"16k_ar": ["ar"],
|
||||
"16k_es": ["es"],
|
||||
"16k_hi": ["hi"],
|
||||
"16k_fr": ["fr"],
|
||||
"16k_de": ["de"],
|
||||
"16k_zh_dialect": ["zh"],
|
||||
}
|
||||
DefaultModel = "16k_zh"
|
||||
@@ -26,7 +43,16 @@ class TencentCloudAsrAPi(object):
|
||||
region = ""
|
||||
self.asrClient = AsrClient(cred, region)
|
||||
|
||||
def SentenceRecognition(self, engSerViceType, data: str) -> (tuple[bool, str | None]):
|
||||
def SentenceRecognition(
|
||||
self,
|
||||
engSerViceType,
|
||||
data: str,
|
||||
data_len: int,
|
||||
filter_dirty: str,
|
||||
filter_modal: bool,
|
||||
filter_punc: bool,
|
||||
convert_num_mode: bool,
|
||||
) -> tuple[bool, str | None]:
|
||||
req = SentenceRecognitionRequest()
|
||||
req.EngSerViceType = engSerViceType
|
||||
req.SourceType = SentenceRecognitionSourceTypePost
|
||||
@@ -35,7 +61,11 @@ class TencentCloudAsrAPi(object):
|
||||
req.SubServiceType = 2
|
||||
req.UsrAudioKey = ""
|
||||
req.Data = data
|
||||
req.DataLen = len(data)
|
||||
req.DataLen = data_len
|
||||
req.FilterDirty = int(filter_dirty)
|
||||
req.FilterModal = int(filter_modal)
|
||||
req.FilterPunc = int(filter_punc)
|
||||
req.ConvertNumMode = int(convert_num_mode)
|
||||
try:
|
||||
res: SentenceRecognitionResponse = self.asrClient.SentenceRecognition(req)
|
||||
return True, res.Result
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
"data": {
|
||||
"secretid": "tencentcloud asr secretid",
|
||||
"secretkey": "tencnetcloud asr secretkey",
|
||||
"model": "model"
|
||||
"model": "model",
|
||||
"filter_dirty": "Dirty word handling",
|
||||
"filter_modal": "Filter filler words such as ‘um’ and ‘uh’",
|
||||
"filter_punc": "Filter sentence-ending punctuation",
|
||||
"convert_num_mode": "Smart Arabic numeral conversion"
|
||||
},
|
||||
"description": "The integration is based on [Tencent Cloud API](https://cloud.tencent.com/document/product/1093/35646)."
|
||||
}
|
||||
@@ -17,18 +21,46 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"model": "model"
|
||||
"model": "model",
|
||||
"filter_dirty": "Dirty word handling",
|
||||
"filter_modal": "Filter filler words such as ‘um’ and ‘uh’",
|
||||
"filter_punc": "Filter sentence-ending punctuation",
|
||||
"convert_num_mode": "Smart Arabic numeral conversion"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"filter_dirty_descriptions": {
|
||||
"options": {
|
||||
"0": "Do not filter dirty words",
|
||||
"1": "Filter dirty words",
|
||||
"2": "Replace dirty words with *"
|
||||
}
|
||||
},
|
||||
"model_descriptions": {
|
||||
"options": {
|
||||
"16k_zh": "16k_zh Chinese",
|
||||
"8k_zh": "8k_zh - Chinese telephone audio",
|
||||
"8k_en": "8k_en - English telephone audio",
|
||||
"16k_zh": "16k_zh - Chinese",
|
||||
"16k_zh-PY": "16k_zh-PY - Chinese, English, and Cantonese",
|
||||
"16k_zh_medical": "16k_zh_medical - Medical Chinese",
|
||||
"16k_en": "16k_en - English",
|
||||
"16k_yue": "16k_yue - Cantonese",
|
||||
"16k_ja": "16k_ja - Japanese",
|
||||
"16k_ko": "16k_ko - Korean",
|
||||
"16k_vi": "16k_vi - Vietnamese",
|
||||
"16k_ms": "16k_ms - Malay",
|
||||
"16k_id": "16k_id - Indonesian",
|
||||
"16k_fil": "16k_fil - Filipino",
|
||||
"16k_th": "16k_th - Thai",
|
||||
"16k_pt": "16k_pt - Portuguese",
|
||||
"16k_tr": "16k_tr - Turkish",
|
||||
"16k_ar": "16k_ar - Arabic",
|
||||
"16k_es": "16k_es - Spanish",
|
||||
"16k_hi": "16k_hi - Hindi",
|
||||
"16k_fr": "16k_fr - French",
|
||||
"16k_de": "16k_de - German",
|
||||
"16k_zh_dialect": "16k_zh_dialect - Chinese dialects"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
"data": {
|
||||
"secretid": "请输入腾讯云ASR服务secretid",
|
||||
"secretkey": "请输入腾讯云ASR服务secretkey",
|
||||
"model": "模型"
|
||||
"model": "模型",
|
||||
"filter_dirty": "脏词处理方式",
|
||||
"filter_modal": "过滤“嗯、啊”等语气词",
|
||||
"filter_punc": "过滤句末标点",
|
||||
"convert_num_mode": "阿拉伯数字智能转换"
|
||||
},
|
||||
"description": "此集成基于腾讯云API, 具体见[腾讯云文档](https://cloud.tencent.com/document/product/1093/35646)"
|
||||
}
|
||||
@@ -17,18 +21,46 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"model": "模型"
|
||||
"model": "模型",
|
||||
"filter_dirty": "脏词处理方式",
|
||||
"filter_modal": "过滤“嗯、啊”等语气词",
|
||||
"filter_punc": "过滤句末标点",
|
||||
"convert_num_mode": "阿拉伯数字智能转换"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"filter_dirty_descriptions": {
|
||||
"options": {
|
||||
"0": "不过滤脏词",
|
||||
"1": "过滤脏词",
|
||||
"2": "将脏词替换为 *"
|
||||
}
|
||||
},
|
||||
"model_descriptions": {
|
||||
"options": {
|
||||
"8k_zh": "8k_zh - 中文电话通用",
|
||||
"8k_en": "8k_en - 英文电话通用",
|
||||
"16k_zh": "16k_zh - 中文通用",
|
||||
"16k_zh-PY": "16k_zh-PY - 中英粤",
|
||||
"16k_zh_medical": "16k_zh_medical - 中文医疗",
|
||||
"16k_en": "16k_en - 英语",
|
||||
"16k_yue": "16k_yue - 粤语",
|
||||
"16k_ja": "16k_ja - 日语",
|
||||
"16k_ko": "16k_ko - 韩语",
|
||||
"16k_vi": "16k_vi - 越南语",
|
||||
"16k_ms": "16k_ms - 马来语",
|
||||
"16k_id": "16k_id - 印度尼西亚语",
|
||||
"16k_fil": "16k_fil - 菲律宾语",
|
||||
"16k_th": "16k_th - 泰语",
|
||||
"16k_pt": "16k_pt - 葡萄牙语",
|
||||
"16k_tr": "16k_tr - 土耳其语",
|
||||
"16k_ar": "16k_ar - 阿拉伯语",
|
||||
"16k_es": "16k_es - 西班牙语",
|
||||
"16k_hi": "16k_hi - 印地语",
|
||||
"16k_fr": "16k_fr - 法语",
|
||||
"16k_de": "16k_de - 德语",
|
||||
"16k_zh_dialect": "16k_zh_dialect - 中文多方言"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user