From cd430ff498ae512eb7256b81d66797a6ae7c1502 Mon Sep 17 00:00:00 2001 From: Howell Jiang Date: Sun, 13 Apr 2025 19:34:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=89=E7=85=A7ha=E6=8E=A8=E8=8D=90=E7=9A=84?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=88=9D=E5=A7=8B=E5=8C=96=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 27 ++++++++-- __init__.py | 120 +++++--------------------------------------- baidu1.png | Bin 0 -> 26890 bytes baidu2.png | Bin 0 -> 21785 bytes icon.png | Bin 0 -> 10598 bytes manifest.json | 4 +- stt.py | 129 ++++++++++++++---------------------------------- tts.py | 80 +++++++++++++++--------------- wake_word.py | 134 -------------------------------------------------- 9 files changed, 117 insertions(+), 377 deletions(-) create mode 100644 baidu1.png create mode 100644 baidu2.png create mode 100644 icon.png delete mode 100644 wake_word.py diff --git a/README.md b/README.md index 992ccc8..bb14c2b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,26 @@ -需要在百度云里面创建应用。开通语音服务里面的 短语音识别和短文本在线合成两个服务 。 +# 百度语音服务集成 -在集成里面添加baidu_voice服务即可。 +这是一个 Home Assistant 集成,用于接入百度语音服务,提供文本转语音(TTS)和语音转文本(STT)功能。 -不会python,cursor帮忙写了一个初始版本,有空再来完善。 +## 配置要求 + +1. 需要在百度云创建应用 +2. 开通以下服务: + - 短语音识别服务 + - 短文本在线合成服务 + +## 配置步骤 + +1. 在百度云控制台创建应用并获取 API Key 和 Secret Key +2. 在 Home Assistant 中添加 baidu_voice 集成 +3. 输入您的 API Key 和 Secret Key + +## 配置截图 + +![百度tts语音服务配置](baidu1.png) +![百度stt语音服务配置](baidu2.png) + +## 注意事项 + +- 本集成需要互联网连接 +- 使用百度语音服务可能会产生费用,请参考百度云官方计费标准 diff --git a/__init__.py b/__init__.py index 19d3ee3..09f3dc6 100644 --- a/__init__.py +++ b/__init__.py @@ -1,11 +1,10 @@ """百度语音集成.""" import logging -from typing import Any, Dict, Optional +from typing import Any, Dict from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.discovery import async_load_platform from aip import AipSpeech from .const import ( @@ -13,23 +12,14 @@ from .const import ( CONF_APP_ID, CONF_API_KEY, CONF_SECRET_KEY, - SERVICE_TTS, - SERVICE_STT, - ATTR_MESSAGE, - ATTR_LANGUAGE, - ATTR_VOLUME, - ATTR_SPEED, - ATTR_PITCH, - ATTR_PERSON, - ATTR_AUDIO, - ATTR_FORMAT, - ATTR_SAMPLE_RATE, ) _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = cv.deprecated(DOMAIN) +PLATFORMS = ["tts", "stt"] + async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool: """设置百度语音集成.""" return True @@ -37,109 +27,23 @@ async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """设置百度语音配置项.""" # 存储配置数据 + hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN] = { CONF_APP_ID: entry.data[CONF_APP_ID], CONF_API_KEY: entry.data[CONF_API_KEY], CONF_SECRET_KEY: entry.data[CONF_SECRET_KEY], } - # 创建语音识别客户端 - client = AipSpeech( - entry.data[CONF_APP_ID], - entry.data[CONF_API_KEY], - entry.data[CONF_SECRET_KEY], - ) - - # 加载TTS平台 - await async_load_platform(hass, "tts", DOMAIN, {}, entry.data) - - # 加载STT平台 - await async_load_platform(hass, "stt", DOMAIN, {}, entry.data) - - # 注册服务 - async def async_tts_service(service): - """处理TTS服务调用.""" - message = service.data.get(ATTR_MESSAGE) - language = service.data.get(ATTR_LANGUAGE, "zh") - volume = service.data.get(ATTR_VOLUME, 5) - speed = service.data.get(ATTR_SPEED, 5) - pitch = service.data.get(ATTR_PITCH, 5) - person = service.data.get(ATTR_PERSON, 0) - - try: - result = await hass.async_add_executor_job( - client.synthesis, - message, - language, - 1, # 1表示mp3格式 - { - "vol": volume, - "per": person, - "spd": speed, - "pit": pitch, - } - ) - - if isinstance(result, dict): - _LOGGER.error("TTS失败: %s", result.get('err_msg')) - return - - # 保存音频文件 - with open('tts_output.mp3', 'wb') as f: - f.write(result) - - _LOGGER.info("TTS成功") - - except Exception as ex: - _LOGGER.error("TTS异常: %s", ex) - - async def async_stt_service(service): - """处理STT服务调用.""" - audio = service.data.get(ATTR_AUDIO) - format = service.data.get(ATTR_FORMAT, "pcm") - sample_rate = service.data.get(ATTR_SAMPLE_RATE, 16000) - - try: - result = await hass.async_add_executor_job( - client.asr, - audio, - format, - sample_rate, - { - "dev_pid": 1537, # 普通话(支持简单的英文识别) - } - ) - - if result.get("err_no") != 0: - _LOGGER.error("STT失败: %s", result.get('err_msg')) - return - - # 返回识别结果 - recognized_text = result.get("result", [""])[0] - _LOGGER.info("STT识别结果: %s", recognized_text) - - except Exception as ex: - _LOGGER.error("STT异常: %s", ex) - - # 注册服务 - hass.services.async_register( - DOMAIN, - SERVICE_TTS, - async_tts_service, - ) - - hass.services.async_register( - DOMAIN, - SERVICE_STT, - async_stt_service, - ) - + # 加载平台 + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """卸载百度语音配置项.""" - hass.services.async_remove(DOMAIN, SERVICE_TTS) - hass.services.async_remove(DOMAIN, SERVICE_STT) - hass.data.pop(DOMAIN) + # 卸载平台 + unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) - return True \ No newline at end of file + if unload_ok: + hass.data.pop(DOMAIN) + + return unload_ok \ No newline at end of file diff --git a/baidu1.png b/baidu1.png new file mode 100644 index 0000000000000000000000000000000000000000..1a2a3532ba7c61b0888f90012e10656ec56e7320 GIT binary patch literal 26890 zcmeFZWl&wq+V4wX5+DQ*?rs4>aGwE!yE{RGyF1LF!QI{6C3tXmcXxMZp1JnB_j%V^ zwclHHPn|Ef?yl+&qk8t}*`xazPj~;Ge~+JXGNMTD@ZLc|K_Q8Y2`fNB0aT!%-T>gC zAvs~o)MijnBv9hQUzA+l9Jj7mdle&*K-sA5b7_9 zOlV#AYkqzq4ZIK3UlOVq2`e-z)SEvg5ImX>AULv0UinX%e-scSsr_>~$o2obqWfyl zFFQ=$KT(O4F#pmzU}gin0}05<$!zAZ{z!$e;77Zvm=EgZyRmS^JEAwWg>p+Db`k3y znrhBXlvYd=jPic8CP#HRp_+Wj)J9M$nIZAgu<=N0wyAMXs?D)sgxsKs`~Pr5nN>qS zY_ks4o8@C2suP-2Lxym-pai+Sor-ztm@D+vJ_s3wyCXtik+tW&EY z^sj|h!*xDyZpdGx;QUX2!GPdbQUcqN(2lt4@-XcF6lx-$RnIhxZFc2oyfIPOG{ra~ z0qf32?t4Nq{sW3a?p}~*-DocRi@_WbeCF4aHyU>Y&{vKT3~u=pCQ2KhDUUH3-Lq=7 z1)b80Tp!>}{zfEeAigc3(Y0S`N2ihfTj#Y52|`vI1BDSXe5v0mj(!^+Hp zjU^)~E3-GbqOa38WlwVgAM!Ww1zKL^){DnuGd!78Qy7kuLp4)(J;0BF1?ATMsrD?$ ztqrXsSd?_9&29DN)YM)kwuGJnp!jnhUHFsah*c$+yY1g}?Ne{9%O2&Q+oGP16I(zQ z+OmV+Cj?F0Go4}FR9)|HihirN%*2o6i-&C9RRwUAP}P~Mv8%{<4_Vj@nRSxloiwh9 z+%(sp5c6|w7NkMyShY}EYW98+TBS2#8Bc%JC`r60H5vc5biWa^jh22$_;gKHM!WH0#cboEM%2IH3XwxLA`N_3 zpy+%F9zRF50Dx4#eru;hZPvR85shKQyd_cM$me$KCJyePk6e2d=D&Wu*|<4t;N94E z_i;@)LdggUEa#ppA$xjCYZysM`{mCpcZtwpUG&)6CPV1TN8~M#bLuo8-OJ@3f%Wh_ z?k?6&47c5)3s|&}-*mV^4|1} zML}X|latprLXsq{<%f7me9OSo>Dy-ZcJDDp1#);V(f zSbz>DD1izz?cbelnXxaceiD~;`x~Rd1sq3Pwm5jsYydhn(CQ%h0w8Jvm{(t z@W8i$q^g_6_u8|z2;|kQmsvorCuH#PlhJH!ufCCjCzLIA^a@PXZ_3CyE8n-6)JRsw z*(KDJk-O0^$4^Zqzn$|MniP_?TEMk8=O}#w&rfII;glEecIFITPj=VGQUch!E))Uq z*#HZZPHd;UcIGyp2*lnZaZEbic23Ys=omda7Ckt{FRw|rT|pGA2dx+BLafa0D)OLWf!f5^i{V@=kLvazk~v5I z?i~LUOwSymW;?ecZp(U_*W%5tqqQR#=@cja79Bgg%^Fz^B-fL;l1t3`z}u0%)=^Yv zQ_IS#)9x7OpX*_&wC~RHUm~BjAIf&S6g9jPsMh)DUWrziFkA2)Ycy*Gn7v_h20uzT z9zM=Fl-BJxJZvZRFSN@KvYaqeC{#PqV06sN}m zYiWxg+1u~T_;o6<3+~2~>o&ko-X8AEEIoIt%?*y%YXso$Tcb?)&aV(Z*|}xx@noD- zlvH7qvg30?avqIv+yKyTHTDX7u+q7~P65Bp>_g|sZlY8O= zV7S9ed^6b9T*9Khxk8f*K%eyIz`5b?amJT7-ngT2WK5R|ADaljlsO#JwT^$*PEolt zYWiw3t`q-sCI4cf{z~N8Gv$;MozQ56_NWlb&Z50^ZtQpG71qvnwZeG6(N>S`Ajs|+ z{7@J6>R_M^y_O?)dIOL((DZhB;ZEtsNp@FVy46SDuaV_{KE|+itF$iu)XY_h80_5Y z(kBHqE%lMv3^9p`Q5<``E~&kh{i@;Fs)z~va_3DX`4Y(35|S-8L2=$Jk@=g2_Tk3! z#A&PjrHJv~7%U`}aYHmCvD#k{QCmN%S0_jZ)Fc=K?gKwuF9;mvXOZ0o)^!gkg6|{1-3XC@X*-RTcQ= zE3eSwj!K+HP-x+wnE2p_mS&~3}&T# z7dv_I#)^G+v6-C)dF&nK3gWbdaE!`5Fsin#I^NauF~}X{f#Gg9chG?9AWqI!Y_!9z z>0&*2SU<(_F?sAuP0+A3pOqiYMOgO*quRNFZ+7!~oVv|ogSGy%{`ZL4ZuFaNN zFDX8=iLR}9Y~M~K?sSjK`=o!cl+ShvZy#?!&YJGAe7TA8AN%@JekHy13P9G$~|~CNyx_Nd=mb+01zF)Xd5=s9O}Y(;GFtaejSfD#dGoJT7CCO_-equ zr{e0dN5r|2S+Sj~OxA&CAw{e{f zrbNH>#(5+8C3E@eww9308|>B#>r&<*8SzX6*Aji}6j-5ZCwK=FepAu9%madU^%O}G zolop^m-F4mtD>qqNJ%Rht!neNT@b z26-r$eD5S=+&(F}YP_9zS8M-LD97YHUswwV2>ZSJ_3%oPFr(~ipO0hr!CWo_I4{B8~Wu;1g?a07h&)oD?RyzOi-a9X zEC@Zo1JkS;gsAnDipbP5SCQAC>(qB1E9={Emb^bXNWPdjD~Yk9n4C4My-Vu~r#h2; z5n{>4rL#RINl(ASrZm%vwTC`NvAEyfkNE{MuFJopS%jc6ZAA^?C@* zP2L{9So#iv445*YC{Q6J0>0(>D7Rs3UO8fHRpiBe8hQ7LuTX?nmbKW14QifV>sHXT z+3ycz0f4e{(zd&}mSP^y7POQ|WSZVR8tu%HcP!9Oj(c}&Ch~UaOC~IY8Hr_{qnS)( zR+*y!&hM9DYEQWUyGKNt*HYr+g`M9e{!{1wcls09j2^U3{1<)*!HIql(~-Nk48r>x zTCA0ZLNByb5`%{(f%;QI^@5HBZZwdZ{W%kIJvL)BEN z#57P|JX@NT=WNEeWxv}ojOV$eN&SlxQ_9b}v~QSN)Sk1+O^TF{;yVlWqxlRGg;HU-Tbg!`6^Ku& zbj~xu+IGC~Mqw7?%B^bUv=0`|cgNegJ5Nu8w1-7JT26DNYQ0&hFyP`P>0L^`vjC7ycj)Satu|p*If$?v_rbf6B|=_ z4HZSUf)zKrcNLW*c0{n%U56w&fYPc^uO)uh9iQ;}CU(DftKSGa4HJ9|8T9qy zdpAYZXlq1pnK$5uo{n6m1kFVuTy|axIu5RH5LRI%orB5BwAD4azUexyU5M%F&UEYd zFYRnoGpi2q3=X*RMyo7do6evxJnL{A&m57DdIX&2?$DXr;TddGh&v#8`l0TzVK#a% ze{7(5=47?wdM#T{5jo@J8K9MTp(yyUp;Bup-wpq=M$kD-ZL+paPDs6hcM>We}% zlZ6&9J#tG`({TWZ!J^T6ZFdrT^G#+~mhuISM#jrn+u5ntt2M3@cGQJYgJNkmi$#j9 z7Wroi@iRgba0l%tQeRHA@+X3SeuV^qwBh%yDR~9xTEF!KalWGGT5YZ|Ch6auiLQ%t zF>4Mm#V6=+{;ay(ZME}OxM_itF(FR|$v?Md^zMrl*bB-OGRD?00vHAur_SjWuDMbj2mq~?_v9fq6O)Dy}& zeq8PD*m&{>yvrC_{8-g4IVS-v@0s1&tI3CV!j@7^u1 z7RyXE#Zj_zN$an%U!--}ng;gBkA z0T$yv;ia@-k${r%5P5*#m;lp>9JlF`TE~$Wh%(4(?kn4166cQEV}#v=?+PQ)eFg zxo1fIet?BX&jvv_aPLLswEY{luF_RQ+Qs{scozGeq_5^a4-4#;3N1&&dxN5D#(`_) zv9qObitpyzk<8bN9X`|{tci!>!0nIo6vwB0Y)G0Jl6i}8UH!AY_KMA+zn?LG&iq4O zRCU2b;afHiAqwO6wuF5N=mEukSX;&gdQ6t@byYSJ4iBTCI7YJZDnRz+P6ulOa9D^& z;|CG)7bUqAYAVc--NFlVmv~JWrpVcB!>cVsURxnpO2u0ja-|BEfCBGWA&fXI%=q6- z@W)|3kwUe39f-9Y0qtPRZ(Kmj9A!X2y|Cd zVTNHfuXn>?a$FLexSiyIQD#y9D>z?H7 zW;=fNBC(BcsUS*#b>@Xk`4cPaW0@83(~BvZ#Yx<-WD}KHi_<%$GVbQ1 z%j_(=IU{TBs7UJx154)d8L){w7yix*SFD|1dsUFoRkG{;yLHP6{_ z3#^TiKr?oKi9=OIeRm9|$Y>y$UzJPaD8yZ4xG|n*;fRrR)RuVPp(BF=X2+ER3o|}^ z($Xg8{>Tr=9odUAo&TZ%vwJmoh*#Vm#||Nz&&P9fKA!HC(2(UE5h%b+^q22_!sc{J z^=-9q@md@Z2tAWJ-RBf_JS?krRe!E+@vxQRX-JS6^STpDnt~jh15WjWxBJv!GPZ13 z9)KV3{eE8NwO+HyfbhAK_M_Shtv${6+lV9~(;D5M06b(FnHOTtjyTz!O;d9nPJtGT z`kNZvJTggKOlu!if%HpR*=TgJ4^l3mZa290nW7IRzfe<;62i6_Hp?Z535U!RY~Yvd ziSa$fbI!<{)hG>zurBZmPu|MltnxBWg%_NB41-r)(xPEX@`KCNY_jN1I55oGfA$3g z2bI+k8D{pbp5si&-l+6^dK>S;!4w%O+su3DQf*%XfZ2d%haWN9`%;B3k-07++v>v1 zV#ujIK}Abc_NGS8Z|eF;;T6yZS+k6@(tTiE23)_1r#lAy8klP>hIjt%o_VbV-N-_$ zF4-;aZLw#SXr2D)Q8vZbK;&7rlVvNUMJtKhI+r8&E?>i=ih);9@G}~>+E7YESN=<( zy#3fohas2q6kcKpdQG%@rF0^3&h>9~)rr-_KA{Kt`l}(QmRNX^1+t7%tI3l#cE1I{~;IKQ(;?4$Rx`>01&G@rE*CJo-?`hG@Bbg8bg zPTft34ktjJ&TDh?$H3kL#7({;1o~et<=<$mT)_3@(_I5sZ2E2`i6>(tfuw9&1wFHF zk(c9C)>8{!XK#85IbZLNHeDXjwLh{dOX#dA#lL~s0CcT_Z%POkG!+QRvoaP~i56B5 zclXA7YX$`z^J43HZWH@49YeukRuX{+p}J3>_m1oB33>n zjBYo2zgqYiMA1gr9cZ^wh3wW*hd_M2gV;T+-xpV$`%7<+_04#?CB*L`wE!L7O$A{y zcY5v`GNpLh<_Iv5shpX)cWVJMEi|r|P0CU* zCnV$e_y5oeu}&GxoWK5{0Il4>F*o>=IR{+2z+v>AqxkZu=;IfvdIm9d%Pe!fZ+^g_7MAiDqFy#Hnd1S5X#v}0(u*t3j(E&saZn|7WGbs8|^JxS1{ zzt{3@;j=6r`Nlry&GWB@($`#OPMPmMt5sApX0Q2d-BT6|2R#zD3lfvKRBR0rFe5!8 zVTR$ZF=m4sz?+#|Oo0;EbfUbZ0_$HE$MGM3Ga^1|7C$xuo3O<&DL*+%NF@(G@@M8o zb7&3fvMA!SwF9Cxf*7|m&bG@Fi!=w(RMjdc8^mp+B05-@s;3&g>-7-~&PDurYXW7G zFU*}@xpt2EovUMvz~bh;Peu%P&?go;9B@>G<#whW5;ELh1^lNgBU>BL&kevRzGyd`WH4$WUx>`eP2KW=l_ybwQLdb_A>-?L)WxGSrgxgqtA==-N zg(8G}NKbr&`&;%IL~nJ-my#{=H)J6VAs=jCeF^`T-6{&-)%I0!La{Hy)=0RL0;|3(kUjWvMcwl|IqD&ip&2t`&vL0N~- z-YTxVjAAXkRT*4>tQ7bS0l-bMZtY0Fhb%2HZf7!iYiQ0}BiQeI(8cWZLA%hpZ~!xD zP2$pWHFpQA+O2nP_aVG*s;dPjq9CxYYet^0Rq5CLsXO-BnM#j8iVn{!p>*we2T#(E z^v9(w>wX3lma~B!AfA)U#%8m1%dMMldlW36EA^0ox0~U;+EBZ!w^`a6k(~=S&*MWx znSGDWKS&k`74kB_i>l(LJzz+a#Rh3=2PSbsD zq%PhJN6!q47A#t4wS>$R*69$)?7`kzeqj5l;QNe*6`PzyTd3Rj9lhOFAF7FIF~y!v zlqgz0Ob75J&&}oTiX;kdiFHK##o~oz^75mV@bRJFP6V#(d1|sJ9_Q)7@qs~71`K}P zOojlTpySZ0-959t+wIT(;{lEWjWjWekj)CWS-={a<|DpRsStEtbk*xn1!Bk$BptOsYtZ4AvAFQ^ z5lmBf73Cn`SWdrzlj{W~E(x604&WGV&_H zQ@fncSt6;cJ|MoP>sGZ{Jg8hltZq+PCzB4@c@1`&x74^UC5>@iaq-x$4(_Ht z;Hw;aBw5-*h)>RZ)PN?u!iu9tasV^=MBJ5R3su2h za^{&qqxqPPo=fkRI;^9leAu4WfMQm-{@zjs;_$gr(if+``N&oNVNKKc4%(cs82Wt4 z00~M1J==#ftoZG<&P_Xd%<;u+u*hvr!#lZ;fuf^A;k*|B5`5Zo0ZF^NRHtLmTEbdevzpQi#6A~);U8e);Fr6 z9^)&{{{~oFpz9l9`XKX}iE*S9=hf5^2A1&0POr_Kn%?O()tk zl$5ij+lH25v=NPIH$VE~E_n`9PrU11n@U>qxA&vUE(4PIPEI+Svu6Iv<|N-4;)I=3 zQLFU`Th`A;8mK|l81DNybIU(s_^pw(>{kR`*Zt7&*;WadA4S5{Iyr4-Zw8T4E7P}xzu&szFdZ= zT>e-{U_CheG2=bM&{jCA{i)Bp1WR|`Ht%Awj$uTOR-A=h|9z3No5-W}$Ytp#(;me^ z?u%IFPe%(s=%naCVzxQFj$KJ>HoI1wuni&><1S=>RtcA{=JqeQ#Uyq7^bxGC~SD zl$6GcZiGeH?X$y{D)Bv)lo8m;)rz}MR7z#i(2)0rt|vlE4ZJfq8^}^NX#rSlTu@2& zbEgWV=KtR0&$jsXRANFRsl}AUz=4f2Ao7ox`IkPS51c zV#O?`=A%@so>nQSRI5}BvTI&DAGO&OodL#6;RU(AG8Un?Opqa2()E5ScXjl*@?Jbg zzWGF|RE7cl1{+oN3&fR;T&)~H&+z&O zR}0zDS+ZMKai9Jln4iBvaji=FV&SAk5{sGXEWxwFyNJt7RXb`v2|lssiZjox5kHME zPYd^)dsc+*G!`LogY_SAN`OUrHp#kJ=7Mxoh40*!Lo$qRI%%8I^hp>^<|Dz zdnu@dkc?VJznk;-vrO(mb%G}jWDnM9QpY~D90f2tPbV6yVgDjZq27lcRGf^7y#q8Y8%2D0*YVH1KEOtK!YOSiY1_!k$vSW^EyWWxM0QM~idNs-~iYegT z52L^G6E+>;PW*(dfFR8GwVYr5YZ%57tp(>*i4&RWdo80hcQ))ok85FL-|E6UqrWi0 zJ^rHX7ztb$?Bf?Wen?x;@-PF)b4+X>L+rpj)7ygZ$P`VG9}& zkv8-Nf=*)QMiT1}%d#2f+5HI|D*Gwn zG}^*hwVWr*EO26LPa1vu@y&+4(IT5> zjRMN6rOL*Wz4ut8`Gf-6qYc{YK@@)2H0S~OFFgkqlXrYrI<{0GqVESfVwqyK{;jIj zTQ>05N(#_r6m>2z3cp?Qic-V~#~H4tl=T@QNYU6O)AB$fdhc>Z-l)fbl;9uqFbqD< z)I;?4_+45TOEoYr{B+>q_TC-4J+}&V)m7E&P;?*#s9#qcLLd!&0Z@`=p6Mo^kVlBU z(C>3~aK?ofePJiVV@rD=k2lTy(FA3i41fspZj)gyV0(5uc1)6((fXaWP3G9}LyNtg zM-x!#SHAj~r4$)wOKI$BT5Pt3W5H~|=^p+NreT zKhv#3p(vrsK9IBagKV-bIuR*mdG5<(@)AP=9`d!g`6HXwmz7Jv$@wtO2!WM+cU2P! ztdIQFAw2ep?=q`mE#r9e#(nqer9`!t@3M4^9(ti@89kAtW2*?)USKPaDpO}`!!f$( zIbZ8C2o`nU2M7f9?^5TwkiT?oVu`JV2j{GKyvH9K@n*C|Fj=F!kQSd@MRf7~>fQ>| zN3|UkWicH9x}V$Z;{QzWgW9y#&?4l~qd4Ey#A_9vI~m2E*mW>4qIzoz)N%$dAAx~b zxbj6YS{&w`UwQD@P3=Qj`mH^DZtVBMkZzK@Z*Kri=|BeWnV^Rkl#U=9WojAUvWYgH zloO#k(N~U?w=i<@Jq`%M@Zic67UnPr{_^WOC^ zIt;PRr7v3R5E2@`bcrZ(0~s$iYcmYn46cPCBi-eF*^r#8UGFMM2!h=Y6G>^LJLB-vQBgUiB+`- zLf36pJDxjmRon%_=w$xzbQmT8D{^_wN-#C~V=F(JjvhKysV%gq(taq>-wcc7aI^e@ zUa0c%*AYiqhrIo>6=l1YOe&i`Eq&|yQb$r52aR!YeA~bCylm3y`xs!u+>|F&^y>=MRo6#%@_A1dYnOpIU=4VwP|z~ z{{)~p*z^AcpirB`u9SO+$Bdxjr4;j>?!*c2*<<{gYJ6qf$RA>m(}CqpnA0~~Nc%6C19U+6PWlw@t|OtBRb(PLzsBsKKinpGNQa=8x-7}aQ$5oB$KkWh*t%d%|eJMlQrdXv<* zHBJr3*ZO(CEGQ3J5|TAn)SHC+Dk<$hszKi=Rs}cQLesKq*58UN-ndSE%y7w>tkd{+ z#Pf4D3?%+?u;d358kUT`aIh?(S>?y<0i#t5};MlO$N^b3QH4Xfd>;L*}Z(FKYr76?=Fbyt7f ze)osW=rBTAbF{3QrIsIiB(RiXM!xow1v%W^x5vtau~#2q#=ndK6to^mWjneF<-_f1 zlm1O8)mMl@DlJPtZ=xM&ML*EB(AVamKQ!3rlOmQXNbyaSFP$hK-DGE3^ExyV$RDfr z=FdlRK|!eVstCERUdYlFQpCuR?FhHqCr_%UUr{BZSCewDkY?u9qZRjBC&bxn&GZ|3 zG>E-N*zWWHmkR5@&H)GUf;?k^5b)L)O+VR8hRwP4yBJOf$;jYGpg2jgY!|bgnZa+{ z=}tr?0aRq2%zh3x$XV!r>bM{mL7)G)oggQc+wRNS=|GCm6iV_4tQ$S&Vp*1>62F5p zB7y^F^I%`Q_)l1b=|~#(zew_ctnkM5)M24{wr6Zva8xLi=#!)DRWaRjb7&8YeNH@~_bM?*2nMG?odd z^P&jC^XN_+Dg>iaNzu~!9*%o&JE83Jjar!mm`;Xcs+ZGbE> z{x}WcOYn)3JP(;6l$v)xdzt8ZjCYV|k|J{Lf!r95?Gn1Kv^FE7br+&saI5J1F%erj%I+K)06zr z#lOTRfINU(WoUV9)QofTZ21~evp3c##2K@kWP*V@B!{&6Z&2n7yo5aurb%?(bMuKy z(2@?je5M`imcNJn)6o1eI#8gOSYT6cVMbur&Y*#yVYLq{8KE1%I6T3O(;U;=!TqWAhW~}bMR=O$KUM@ms$AUAca8vKF}6Le_B=H!+im#H zg?o!)FnFBH+i^t(5b8jPIeta!>bkYz7unT;%dre1T>jb1v#AV? zy@o)7^wDsySA!$1s(*xrfh59^IGCVesZ7XXwmmJk|Nq9|2kKEo00-#wc{MIoCT$Om zex|U)l+8oj@*nVe>XZdFfvZ@W+Yc(EtzRi)&NAYaL+HtFXO}j!&k0w2=^-C03yDpbn8HimLI!Is3=oTc z#{?_8@a?5w%)dXYJjKU-4+8z&v~xc{99$XXtnse6BSQif-HKlngiTkeIsIu&Z&1?HqERA}MCXFrpyi#G`8(yo_-y5o|T-m&7HnqN?t(wuh`=Mi%_MP~w17H;cPcvmBqq zRj%Z3Bi(#Y2dEn{5J}|da3NIByP=W-jD?k;SwvFtwMYj2`+fpL%NIys2!q$| ziy4mKng|4s_d8E|DUWd**CtQ}&ckB6tsyF%f#pw04l>m5ftK(06p@ho;f>Vh3iZcfEXIv zW7rrBY=In1$OIy4c;?%0P3BXGdt@=JLgM7NQRD2yxSq82!#sC&hs&@AHszg?I*XMx z_?QhU5J?{lt$7he(-uVCaI_BVZQaI)5SunHMBKWIm3r-Jzn*-AH3&l;I$wCP8#KXT z){Wi^s;Q@zlsLUtXK9rq!ug&}zc;K^v#Dyq7?=q7KbNi2hs~8+ng&RSCs&~vh?PyU zOnRHg8~cmXrz_0auk6|h4LBLlq{!S78=yL#za~JI|HvMLnWY(KMt5#32xX=$E zVeFxdU1-y@Id>e4;#n#X6{ zKnth^%kYtrV=ShjDWF<7Oy>%Dtp{=Y!1drc3NbxEk1i5WJ=0FUO5G5whQPwO3;3426>l*QVQgB(tIf(%tapNVUn-1}9D$Fvn=Ks6PK(lbzIc9G;wX(cX3!#(`nRc3X84wmJ3RI#>lXlZxq&GYY+_Zh~TQ2ek}5jr*>Skobn7%zil= zJ^O}hNi{~mM^~rLE9l02=*0GuP~*~~R2CN1boJV|GMX%0##2;1&7&UW9@m~u_a_pj zBY2r^5id)XZ_Ay6QaOL3Z9!e}MCA|Z-0D4p0Pk{U7HMjdvlrLPxQ?za!iym5+tsNp z*V(uG@RSE?(DKS%H}wkZ64r8X;R>Rkh4JBvkFe^$ij#OF*SbyxG}$pWk}aqB?KjSR z@4;{nP}Ct=1CHyknXjZ0a7{~x>uo8wdz%W2fMQNb+7&Lx9`pi>V*OlnsJOrHYbrSF z7M z8PLqGOa^~(L!S>#7@ST_%9lyM%;YE^#z4tX+ye?YE?14pu4d7!Z-{YURl36ul)j@0)(=*pZ2>0-ll?=;fJ$_8%ls@wq&HQG#}HAcMoxb_B8U%i0KHn6NzwD@ zokh_Ug9P*if4o!$E93(MDI=4&B3^H+%}^hNq~2YAEu0Ej@*PU%37VC01;LY!@bXUc zP~?W-F^TSkouBWc2g%-FAT^LXcA#^UKTYlRL5CA&kdX<;@Y!7_pM6N%Mzwk4nzQcp zsD<&ht))tE%|ZBp>|I`u>0UY)5Tf9IlzDe1+R6Dle|DVNVhsL<-GxQRP?d)Uw5++4 z1U-YU1l{_Ld`d0%&8gP~0qs`~_Ypi!(|naAAemJUds~ z&c+r6@QXb3k5eF=(El-XT&KZ z%tqmwh;U9mi+A^DBqcd)5}6nvUE-3CJxN>UJnt_81{NylhjPS|CL>?@8SLL41C^7O zfWrDDH!6ud$F?4-(<2jR)bPqE`E=@nU*CvYhTp0mY@nLWJZtlel4AvdjFo)SVhu{V z7ZzU}(aHKdmb3ZnV;+E3YWfyLW8bO0mlz-r(QD%a*1S}1?1F#@HOY_x1Apf3SD^&nLXP`}l8?NBMW(InA&FmDT{2ys-q?y7IJ+=l#2XHiy zPTY&M2IOoz^>N^la*ily+}_OL9C)Jf-8L3*tDv+m2Bi{UZ;%olknbvzn%DOUmrYNMogdQ$yg4A&9PT1

9IVbIM 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 VIO#~ZCapZ(W{)AD%Wcln{{SVHQ_27U literal 0 HcmV?d00001 diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..794a77e8b15269cf29e9e037ca2e53910a6034cd GIT binary patch literal 10598 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.16.10"], + "requirements": ["baidu-aip>=4.16.10", "chardet>=5.2.0"], "version": "0.1.0", "iot_class": "cloud_push", "config_flow": true, diff --git a/stt.py b/stt.py index ea072ea..3166f63 100644 --- a/stt.py +++ b/stt.py @@ -1,6 +1,6 @@ """Baidu Speech-to-Text integration.""" import logging -from typing import Any, Optional +from typing import Any from aip import AipSpeech from homeassistant.components.stt import ( @@ -13,8 +13,6 @@ from homeassistant.components.stt import ( SpeechMetadata, SpeechResult, SpeechResultState, - Provider, - async_get_provider, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -24,8 +22,31 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) -class BaiduSTTBase: - """Base class for Baidu STT functionality.""" +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> bool: + """Set up Baidu STT from a config entry.""" + async_add_entities([BaiduSTTEntity(hass, config_entry)]) + return True + +class BaiduSTTEntity(SpeechToTextEntity): + """Baidu Speech-to-Text entity.""" + + def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None: + """Initialize Baidu STT entity.""" + self.hass = hass + self._config_entry = config_entry + self._attr_name = "Baidu STT" + self._attr_unique_id = f"{DOMAIN}_stt" + + # 初始化百度语音识别客户端 + self._client = AipSpeech( + config_entry.data["app_id"], + config_entry.data["api_key"], + config_entry.data["secret_key"] + ) @property def supported_languages(self) -> list[str]: @@ -57,18 +78,26 @@ class BaiduSTTBase: """Return a list of supported channels.""" return [AudioChannels.CHANNEL_MONO] - async def _process_audio(self, audio_data: bytes, client: AipSpeech) -> SpeechResult: - """Process audio data using Baidu STT API.""" + async def async_process_audio_stream( + self, metadata: SpeechMetadata, stream: Any + ) -> SpeechResult: + """Process an audio stream to STT service.""" try: + # 读取音频数据 + audio_data = b"" + async for chunk in stream: + audio_data += chunk + if not audio_data: _LOGGER.warning("No audio data received") return SpeechResult("", SpeechResultState.ERROR) + _LOGGER.debug("Processing audio stream with metadata: %s", metadata) _LOGGER.debug("Total audio data size: %d bytes", len(audio_data)) # 使用百度语音识别API result = await self.hass.async_add_executor_job( - client.asr, + self._client.asr, audio_data, 'pcm', 16000, @@ -88,86 +117,4 @@ class BaiduSTTBase: except Exception as ex: _LOGGER.error("Error during Baidu STT: %s", ex, exc_info=True) - return SpeechResult("", SpeechResultState.ERROR) - -async def async_get_engine( - hass: HomeAssistant, - config: dict, - discovery_info: Optional[dict] = None -) -> Provider: - """Set up Baidu STT component.""" - _LOGGER.debug("Setting up Baidu STT engine") - return BaiduSTTProvider(hass) - -class BaiduSTTProvider(BaiduSTTBase, Provider): - """Baidu STT api provider.""" - - def __init__(self, hass: HomeAssistant) -> None: - """Initialize Baidu STT provider.""" - self.hass = hass - self._app_id = hass.data[DOMAIN]["app_id"] - self._api_key = hass.data[DOMAIN]["api_key"] - self._secret_key = hass.data[DOMAIN]["secret_key"] - self._client = AipSpeech(self._app_id, self._api_key, self._secret_key) - self.name = "Baidu STT" - self._language = "zh-CN" - _LOGGER.debug("Initialized Baidu STT provider with language: %s", self._language) - - @property - def default_language(self) -> str: - """Return the default language.""" - return self._language - - async def async_process_audio_stream( - self, metadata: SpeechMetadata, stream: Any - ) -> SpeechResult: - """Process an audio stream to STT service.""" - _LOGGER.debug("Processing audio stream with metadata: %s", metadata) - - # 读取音频数据 - audio_data = b"" - async for chunk in stream: - audio_data += chunk - - return await self._process_audio(audio_data, self._client) - -async def async_setup_entry( - hass: HomeAssistant, - config_entry: ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Set up Baidu STT from a config entry.""" - provider = await async_get_provider(hass, DOMAIN) - if provider: - async_add_entities([BaiduSTTEntity(provider, config_entry)]) - -class BaiduSTTEntity(BaiduSTTBase, SpeechToTextEntity): - """Baidu Speech-to-Text entity.""" - - def __init__(self, provider: Provider, config_entry: ConfigEntry) -> None: - """Initialize Baidu STT entity.""" - self._provider = provider - self._config_entry = config_entry - self._attr_name = "Baidu Speech-to-Text" - self._attr_unique_id = f"{DOMAIN}_stt" - self.hass = provider.hass - - # 初始化百度语音识别客户端 - self._client = AipSpeech( - config_entry.data["app_id"], - config_entry.data["api_key"], - config_entry.data["secret_key"] - ) - - async def async_process_audio_stream( - self, metadata: SpeechMetadata, stream: Any - ) -> SpeechResult: - """Process an audio stream to STT service.""" - _LOGGER.debug("Processing audio stream with metadata: %s", metadata) - - # 读取音频数据 - audio_data = b"" - async for chunk in stream: - audio_data += chunk - - return await self._process_audio(audio_data, self._client) \ No newline at end of file + return SpeechResult("", SpeechResultState.ERROR) \ No newline at end of file diff --git a/tts.py b/tts.py index 342428e..119e24a 100644 --- a/tts.py +++ b/tts.py @@ -1,18 +1,18 @@ """Support for Baidu TTS.""" import logging -import asyncio -from aip import AipSpeech -import voluptuous as vol +from typing import Any -from homeassistant.components.tts import Provider, PLATFORM_SCHEMA -from homeassistant.const import CONF_API_KEY -import homeassistant.helpers.config_validation as cv +from aip import AipSpeech +from homeassistant.components.tts import ( + TextToSpeechEntity, + PLATFORM_SCHEMA, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( DOMAIN, - CONF_APP_ID, - CONF_API_KEY, - CONF_SECRET_KEY, DEFAULT_SPEED, DEFAULT_PITCH, DEFAULT_VOLUME, @@ -24,41 +24,43 @@ _LOGGER = logging.getLogger(__name__) SUPPORT_LANGUAGES = ["zh", "en"] DEFAULT_LANG = "zh" -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_APP_ID): cv.string, - vol.Required(CONF_API_KEY): cv.string, - vol.Required(CONF_SECRET_KEY): cv.string, - } -) +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> bool: + """Set up Baidu TTS from a config entry.""" + async_add_entities([BaiduTTSEntity(hass, config_entry)]) + return True -async def async_get_engine(hass, config, discovery_info=None): - """Set up Baidu TTS component.""" - return BaiduTTSProvider(hass) +class BaiduTTSEntity(TextToSpeechEntity): + """Baidu TTS Entity.""" -class BaiduTTSProvider(Provider): - """Baidu TTS api provider.""" - - def __init__(self, hass): - """Initialize Baidu TTS provider.""" + def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None: + """Initialize Baidu TTS entity.""" self.hass = hass - self._app_id = hass.data[DOMAIN][CONF_APP_ID] - self._api_key = hass.data[DOMAIN][CONF_API_KEY] - self._secret_key = hass.data[DOMAIN][CONF_SECRET_KEY] - self._client = AipSpeech(self._app_id, self._api_key, self._secret_key) - self.name = "Baidu TTS" + self._config_entry = config_entry + self._attr_name = "Baidu TTS" + self._attr_unique_id = f"{DOMAIN}_tts" + + # 初始化百度语音合成客户端 + self._client = AipSpeech( + config_entry.data["app_id"], + config_entry.data["api_key"], + config_entry.data["secret_key"] + ) @property - def default_language(self): + def default_language(self) -> str: """Return the default language.""" return DEFAULT_LANG @property - def supported_languages(self): + def supported_languages(self) -> list[str]: """Return list of supported languages.""" return SUPPORT_LANGUAGES - async def async_get_tts_audio(self, message, language, options=None): + async def async_get_tts_audio(self, message: str, language: str, options: dict = None) -> tuple[str, bytes]: """Load TTS from Baidu.""" options = options or {} speed = options.get("speed", DEFAULT_SPEED) @@ -66,8 +68,9 @@ class BaiduTTSProvider(Provider): volume = options.get("volume", DEFAULT_VOLUME) person = options.get("person", DEFAULT_PERSON) - def _get_tts(): - result = self._client.synthesis( + try: + result = await self.hass.async_add_executor_job( + self._client.synthesis, message, language, 1, # 1: mp3 @@ -76,16 +79,15 @@ class BaiduTTSProvider(Provider): "pit": pitch, "vol": volume, "per": person, - }, + } ) + if isinstance(result, dict): _LOGGER.error("Baidu TTS error: %s", result) return None, None + return "mp3", result - - try: - audio_type, audio_data = await self.hass.async_add_executor_job(_get_tts) - return audio_type, audio_data + except Exception as ex: _LOGGER.error("Error during Baidu TTS: %s", ex) return None, None \ No newline at end of file diff --git a/wake_word.py b/wake_word.py deleted file mode 100644 index abbc9ab..0000000 --- a/wake_word.py +++ /dev/null @@ -1,134 +0,0 @@ -"""Support for Baidu Wake Word Detection.""" -import logging -import asyncio -from aip import AipSpeech -import voluptuous as vol - -from homeassistant.components.wake_word import ( - WakeWordDetectionResult, - WakeWordDetectionResultState, - WakeWordProvider, -) -from homeassistant.const import CONF_API_KEY -import homeassistant.helpers.config_validation as cv - -from .const import ( - DOMAIN, - CONF_APP_ID, - CONF_API_KEY, - CONF_SECRET_KEY, - CONF_WAKE_WORD, - DEFAULT_WAKE_WORD, -) - -_LOGGER = logging.getLogger(__name__) - -SUPPORT_LANGUAGES = ["zh", "en"] -DEFAULT_LANG = "zh" - -async def async_get_engine(hass, config, discovery_info=None): - """Set up Baidu Wake Word component.""" - _LOGGER.debug("Setting up Baidu Wake Word engine") - return BaiduWakeWordProvider(hass) - -class BaiduWakeWordProvider(WakeWordProvider): - """Baidu Wake Word api provider.""" - - def __init__(self, hass): - """Initialize Baidu Wake Word provider.""" - self.hass = hass - self._app_id = hass.data[DOMAIN][CONF_APP_ID] - self._api_key = hass.data[DOMAIN][CONF_API_KEY] - self._secret_key = hass.data[DOMAIN][CONF_SECRET_KEY] - self._wake_word = hass.data[DOMAIN][CONF_WAKE_WORD] - self._client = AipSpeech(self._app_id, self._api_key, self._secret_key) - self.name = "Baidu Wake Word" - self._language = hass.config.language or DEFAULT_LANG - _LOGGER.debug("Initialized Baidu Wake Word provider with language: %s", self._language) - - @property - def supported_languages(self): - """Return list of supported languages.""" - return SUPPORT_LANGUAGES - - @property - def supported_formats(self): - """Return list of supported formats.""" - return ["wav", "pcm"] - - @property - def supported_sample_rates(self): - """Return list of supported sample rates.""" - return [16000] - - @property - def supported_channels(self): - """Return list of supported channels.""" - return [1] - - async def async_process_audio_stream(self, stream, options=None): - """Process an audio stream to wake word service.""" - _LOGGER.debug("Processing audio stream for wake word detection") - options = options or {} - language = options.get("language", self._language) - _LOGGER.debug("Using language: %s", language) - - try: - # 读取音频流 - audio_data = b"" - async for chunk in stream: - if chunk: - audio_data += chunk - _LOGGER.debug("Received audio chunk of size: %d", len(chunk)) - - if not audio_data: - _LOGGER.warning("No audio data received") - return WakeWordDetectionResult( - state=WakeWordDetectionResultState.ERROR, - wake_word=None, - ) - - _LOGGER.debug("Total audio data size: %d bytes", len(audio_data)) - - def _process_audio(): - try: - # 使用百度语音识别API进行唤醒词检测 - result = self._client.asr(audio_data, 'pcm', 16000, { - 'dev_pid': 1536 if language == 'zh' else 1737, # 1536: 普通话, 1737: 英语 - }) - - _LOGGER.debug("ASR result: %s", result) - - if result.get('err_no') == 0: - text = result.get('result')[0] - _LOGGER.debug("Recognized text: %s", text) - # 检查是否包含唤醒词 - if self._wake_word in text: - _LOGGER.debug("Wake word detected: %s", self._wake_word) - return self._wake_word - _LOGGER.debug("No wake word detected") - return None - _LOGGER.warning("ASR error: %s", result) - return None - except Exception as ex: - _LOGGER.error("Error during Baidu Wake Word processing: %s", ex, exc_info=True) - return None - - wake_word = await self.hass.async_add_executor_job(_process_audio) - if wake_word is None: - _LOGGER.debug("No wake word detected") - return WakeWordDetectionResult( - state=WakeWordDetectionResultState.ERROR, - wake_word=None, - ) - _LOGGER.debug("Successfully detected wake word: %s", wake_word) - return WakeWordDetectionResult( - state=WakeWordDetectionResultState.SUCCESS, - wake_word=wake_word, - ) - except Exception as ex: - _LOGGER.error("Error during Baidu Wake Word: %s", ex, exc_info=True) - return WakeWordDetectionResult( - state=WakeWordDetectionResultState.ERROR, - wake_word=None, - ) \ No newline at end of file