homeassistant 2025.7.0b0__py3-none-any.whl → 2025.7.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. homeassistant/components/aemet/sensor.py +1 -1
  2. homeassistant/components/airq/coordinator.py +2 -2
  3. homeassistant/components/alexa_devices/__init__.py +5 -2
  4. homeassistant/components/alexa_devices/notify.py +2 -0
  5. homeassistant/components/alexa_devices/quality_scale.yaml +1 -1
  6. homeassistant/components/alexa_devices/strings.json +11 -4
  7. homeassistant/components/alexa_devices/switch.py +2 -0
  8. homeassistant/components/alexa_devices/translations/bg.json +6 -4
  9. homeassistant/components/alexa_devices/translations/cs.json +0 -4
  10. homeassistant/components/alexa_devices/translations/de.json +6 -7
  11. homeassistant/components/alexa_devices/translations/el.json +4 -5
  12. homeassistant/components/alexa_devices/translations/en.json +12 -5
  13. homeassistant/components/alexa_devices/translations/es.json +0 -4
  14. homeassistant/components/alexa_devices/translations/et.json +4 -5
  15. homeassistant/components/alexa_devices/translations/ga.json +0 -6
  16. homeassistant/components/alexa_devices/translations/he.json +0 -4
  17. homeassistant/components/alexa_devices/translations/hu.json +0 -4
  18. homeassistant/components/alexa_devices/translations/id.json +1 -0
  19. homeassistant/components/alexa_devices/translations/lt.json +0 -4
  20. homeassistant/components/alexa_devices/translations/mk.json +12 -0
  21. homeassistant/components/alexa_devices/translations/nl.json +0 -4
  22. homeassistant/components/alexa_devices/translations/pl.json +0 -4
  23. homeassistant/components/alexa_devices/translations/pt.json +0 -4
  24. homeassistant/components/alexa_devices/translations/ru.json +4 -5
  25. homeassistant/components/alexa_devices/translations/sk.json +4 -5
  26. homeassistant/components/alexa_devices/translations/sv.json +17 -5
  27. homeassistant/components/alexa_devices/translations/tr.json +0 -4
  28. homeassistant/components/alexa_devices/translations/zh-Hans.json +15 -4
  29. homeassistant/components/alexa_devices/translations/zh-Hant.json +4 -5
  30. homeassistant/components/alexa_devices/utils.py +40 -0
  31. homeassistant/components/altruist/translations/id.json +11 -0
  32. homeassistant/components/anthropic/__init__.py +8 -1
  33. homeassistant/components/anthropic/translations/bg.json +21 -0
  34. homeassistant/components/anthropic/translations/ga.json +32 -1
  35. homeassistant/components/anthropic/translations/id.json +19 -0
  36. homeassistant/components/anthropic/translations/ru.json +30 -0
  37. homeassistant/components/buienradar/sensor.py +0 -1
  38. homeassistant/components/cambridge_audio/translations/sv.json +1 -1
  39. homeassistant/components/demo/translations/select.sv.json +2 -2
  40. homeassistant/components/derivative/translations/ga.json +20 -0
  41. homeassistant/components/derivative/translations/ru.json +3 -0
  42. homeassistant/components/devolo_home_control/translations/bg.json +6 -0
  43. homeassistant/components/devolo_home_control/translations/id.json +5 -0
  44. homeassistant/components/devolo_home_control/translations/sv.json +10 -0
  45. homeassistant/components/devolo_home_control/translations/zh-Hans.json +10 -0
  46. homeassistant/components/dlink/manifest.json +1 -1
  47. homeassistant/components/ecovacs/translations/sv.json +1 -1
  48. homeassistant/components/enphase_envoy/translations/sv.json +2 -2
  49. homeassistant/components/ezviz/translations/bg.json +6 -0
  50. homeassistant/components/ezviz/translations/mk.json +13 -0
  51. homeassistant/components/ezviz/translations/sv.json +10 -0
  52. homeassistant/components/ezviz/translations/zh-Hans.json +10 -0
  53. homeassistant/components/frontend/manifest.json +1 -1
  54. homeassistant/components/goodwe/translations/sv.json +5 -5
  55. homeassistant/components/google_generative_ai_conversation/__init__.py +15 -0
  56. homeassistant/components/google_generative_ai_conversation/config_flow.py +72 -36
  57. homeassistant/components/google_generative_ai_conversation/const.py +15 -2
  58. homeassistant/components/google_generative_ai_conversation/diagnostics.py +1 -0
  59. homeassistant/components/google_generative_ai_conversation/entity.py +7 -2
  60. homeassistant/components/google_generative_ai_conversation/helpers.py +73 -0
  61. homeassistant/components/google_generative_ai_conversation/strings.json +28 -1
  62. homeassistant/components/google_generative_ai_conversation/translations/bg.json +19 -0
  63. homeassistant/components/google_generative_ai_conversation/translations/de.json +28 -0
  64. homeassistant/components/google_generative_ai_conversation/translations/el.json +28 -0
  65. homeassistant/components/google_generative_ai_conversation/translations/en.json +28 -0
  66. homeassistant/components/google_generative_ai_conversation/translations/es.json +28 -0
  67. homeassistant/components/google_generative_ai_conversation/translations/et.json +28 -0
  68. homeassistant/components/google_generative_ai_conversation/translations/ga.json +9 -0
  69. homeassistant/components/google_generative_ai_conversation/translations/id.json +30 -0
  70. homeassistant/components/google_generative_ai_conversation/translations/mk.json +11 -0
  71. homeassistant/components/google_generative_ai_conversation/translations/ru.json +67 -0
  72. homeassistant/components/google_generative_ai_conversation/translations/sk.json +28 -0
  73. homeassistant/components/google_generative_ai_conversation/translations/sv.json +21 -0
  74. homeassistant/components/google_generative_ai_conversation/translations/zh-Hans.json +28 -0
  75. homeassistant/components/google_generative_ai_conversation/translations/zh-Hant.json +28 -0
  76. homeassistant/components/google_generative_ai_conversation/tts.py +48 -106
  77. homeassistant/components/group/translations/el.json +18 -18
  78. homeassistant/components/habitica/const.py +1 -1
  79. homeassistant/components/harmony/translations/select.sv.json +1 -1
  80. homeassistant/components/harmony/translations/sv.json +1 -1
  81. homeassistant/components/homeassistant_hardware/translations/ga.json +9 -0
  82. homeassistant/components/homeassistant_hardware/translations/mk.json +9 -0
  83. homeassistant/components/homeassistant_sky_connect/translations/ga.json +10 -0
  84. homeassistant/components/homeassistant_sky_connect/translations/mk.json +7 -0
  85. homeassistant/components/homeassistant_yellow/translations/ga.json +5 -0
  86. homeassistant/components/homewizard/translations/sv.json +3 -1
  87. homeassistant/components/http/auth.py +1 -1
  88. homeassistant/components/image/__init__.py +34 -3
  89. homeassistant/components/input_select/translations/sv.json +6 -6
  90. homeassistant/components/iron_os/translations/sv.json +1 -1
  91. homeassistant/components/keenetic_ndms2/translations/id.json +3 -0
  92. homeassistant/components/lamarzocco/translations/sv.json +3 -3
  93. homeassistant/components/lametric/translations/sv.json +2 -2
  94. homeassistant/components/lametric/update.py +1 -1
  95. homeassistant/components/lg_thinq/translations/sv.json +5 -5
  96. homeassistant/components/litterrobot/translations/bg.json +4 -0
  97. homeassistant/components/litterrobot/translations/ga.json +3 -0
  98. homeassistant/components/matter/translations/id.json +5 -0
  99. homeassistant/components/matter/vacuum.py +38 -26
  100. homeassistant/components/meater/__init__.py +5 -1
  101. homeassistant/components/meater/coordinator.py +4 -2
  102. homeassistant/components/media_source/local_source.py +21 -4
  103. homeassistant/components/mqtt/translations/bg.json +10 -0
  104. homeassistant/components/mqtt/translations/el.json +21 -0
  105. homeassistant/components/mqtt/translations/fi.json +3 -0
  106. homeassistant/components/mqtt/translations/ga.json +25 -0
  107. homeassistant/components/mqtt/translations/mk.json +23 -0
  108. homeassistant/components/mqtt/translations/ru.json +1 -0
  109. homeassistant/components/mqtt/translations/sv.json +24 -0
  110. homeassistant/components/mqtt/translations/zh-Hans.json +26 -0
  111. homeassistant/components/mqtt/translations/zh-Hant.json +19 -0
  112. homeassistant/components/music_assistant/button.py +0 -6
  113. homeassistant/components/ntfy/translations/id.json +13 -0
  114. homeassistant/components/ohme/translations/sv.json +1 -1
  115. homeassistant/components/ollama/__init__.py +2 -0
  116. homeassistant/components/ollama/const.py +2 -0
  117. homeassistant/components/ollama/translations/bg.json +19 -0
  118. homeassistant/components/ollama/translations/ga.json +31 -1
  119. homeassistant/components/ollama/translations/id.json +18 -0
  120. homeassistant/components/ollama/translations/mk.json +13 -1
  121. homeassistant/components/onewire/translations/sv.json +4 -4
  122. homeassistant/components/openai_conversation/__init__.py +2 -0
  123. homeassistant/components/openai_conversation/const.py +1 -1
  124. homeassistant/components/openai_conversation/conversation.py +1 -1
  125. homeassistant/components/openai_conversation/translations/bg.json +26 -0
  126. homeassistant/components/openai_conversation/translations/id.json +23 -0
  127. homeassistant/components/openai_conversation/translations/mk.json +44 -0
  128. homeassistant/components/playstation_network/translations/bg.json +25 -1
  129. homeassistant/components/playstation_network/translations/es.json +1 -1
  130. homeassistant/components/playstation_network/translations/ga.json +31 -0
  131. homeassistant/components/playstation_network/translations/id.json +14 -0
  132. homeassistant/components/playstation_network/translations/mk.json +18 -0
  133. homeassistant/components/playstation_network/translations/sv.json +29 -0
  134. homeassistant/components/playstation_network/translations/zh-Hans.json +29 -0
  135. homeassistant/components/qnap/translations/sv.json +1 -1
  136. homeassistant/components/remote_calendar/translations/fi.json +34 -0
  137. homeassistant/components/select/translations/sv.json +10 -10
  138. homeassistant/components/smarla/__init__.py +2 -2
  139. homeassistant/components/sonos/translations/bg.json +5 -0
  140. homeassistant/components/sonos/translations/el.json +6 -0
  141. homeassistant/components/sonos/translations/ga.json +8 -0
  142. homeassistant/components/sonos/translations/mk.json +10 -0
  143. homeassistant/components/sonos/translations/ru.json +6 -0
  144. homeassistant/components/sonos/translations/sv.json +6 -0
  145. homeassistant/components/sonos/translations/zh-Hans.json +9 -0
  146. homeassistant/components/sonos/translations/zh-Hant.json +6 -0
  147. homeassistant/components/switchbot/translations/bg.json +26 -0
  148. homeassistant/components/switchbot/translations/ga.json +33 -0
  149. homeassistant/components/switchbot/translations/id.json +19 -0
  150. homeassistant/components/switchbot/translations/mk.json +49 -0
  151. homeassistant/components/switchbot/translations/sv.json +52 -0
  152. homeassistant/components/switchbot/translations/zh-Hans.json +53 -0
  153. homeassistant/components/telegram_bot/__init__.py +20 -4
  154. homeassistant/components/telegram_bot/bot.py +33 -29
  155. homeassistant/components/telegram_bot/config_flow.py +49 -10
  156. homeassistant/components/telegram_bot/const.py +1 -1
  157. homeassistant/components/telegram_bot/strings.json +33 -10
  158. homeassistant/components/telegram_bot/translations/bg.json +0 -1
  159. homeassistant/components/telegram_bot/translations/cs.json +5 -12
  160. homeassistant/components/telegram_bot/translations/de.json +12 -11
  161. homeassistant/components/telegram_bot/translations/el.json +5 -12
  162. homeassistant/components/telegram_bot/translations/en-GB.json +5 -12
  163. homeassistant/components/telegram_bot/translations/en.json +35 -12
  164. homeassistant/components/telegram_bot/translations/es.json +5 -12
  165. homeassistant/components/telegram_bot/translations/et.json +11 -12
  166. homeassistant/components/telegram_bot/translations/fr.json +5 -12
  167. homeassistant/components/telegram_bot/translations/ga.json +5 -11
  168. homeassistant/components/telegram_bot/translations/he.json +3 -8
  169. homeassistant/components/telegram_bot/translations/hu.json +5 -12
  170. homeassistant/components/telegram_bot/translations/id.json +0 -5
  171. homeassistant/components/telegram_bot/translations/it.json +2 -5
  172. homeassistant/components/telegram_bot/translations/lt.json +5 -12
  173. homeassistant/components/telegram_bot/translations/mk.json +7 -5
  174. homeassistant/components/telegram_bot/translations/nl.json +5 -12
  175. homeassistant/components/telegram_bot/translations/pt.json +5 -12
  176. homeassistant/components/telegram_bot/translations/ru.json +12 -11
  177. homeassistant/components/telegram_bot/translations/sk.json +5 -12
  178. homeassistant/components/telegram_bot/translations/sv.json +5 -12
  179. homeassistant/components/telegram_bot/translations/tr.json +5 -12
  180. homeassistant/components/telegram_bot/translations/zh-Hans.json +5 -12
  181. homeassistant/components/telegram_bot/translations/zh-Hant.json +5 -12
  182. homeassistant/components/template/translations/sv.json +5 -5
  183. homeassistant/components/tilt_pi/translations/id.json +11 -0
  184. homeassistant/components/tts/__init__.py +15 -0
  185. homeassistant/components/unifiprotect/translations/zh-Hans.json +3 -3
  186. homeassistant/components/utility_meter/translations/sv.json +9 -9
  187. homeassistant/components/vegehub/translations/bg.json +2 -0
  188. homeassistant/components/vegehub/translations/id.json +11 -0
  189. homeassistant/components/whirlpool/translations/sv.json +12 -12
  190. homeassistant/components/zha/translations/bg.json +23 -0
  191. homeassistant/components/zha/translations/el.json +10 -0
  192. homeassistant/components/zha/translations/ga.json +20 -0
  193. homeassistant/components/zha/translations/id.json +6 -0
  194. homeassistant/components/zha/translations/sv.json +15 -0
  195. homeassistant/components/zha/translations/zh-Hans.json +50 -0
  196. homeassistant/components/zwave_js/api.py +1 -1
  197. homeassistant/components/zwave_js/config_flow.py +12 -8
  198. homeassistant/components/zwave_js/manifest.json +1 -1
  199. homeassistant/config_entries.py +6 -0
  200. homeassistant/const.py +1 -1
  201. homeassistant/package_constraints.txt +1 -1
  202. {homeassistant-2025.7.0b0.dist-info → homeassistant-2025.7.0b1.dist-info}/METADATA +1 -1
  203. {homeassistant-2025.7.0b0.dist-info → homeassistant-2025.7.0b1.dist-info}/RECORD +208 -192
  204. {homeassistant-2025.7.0b0.dist-info → homeassistant-2025.7.0b1.dist-info}/WHEEL +0 -0
  205. {homeassistant-2025.7.0b0.dist-info → homeassistant-2025.7.0b1.dist-info}/entry_points.txt +0 -0
  206. {homeassistant-2025.7.0b0.dist-info → homeassistant-2025.7.0b1.dist-info}/licenses/LICENSE.md +0 -0
  207. {homeassistant-2025.7.0b0.dist-info → homeassistant-2025.7.0b1.dist-info}/licenses/homeassistant/backports/LICENSE.Python +0 -0
  208. {homeassistant-2025.7.0b0.dist-info → homeassistant-2025.7.0b1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "config": {
3
3
  "abort": {
4
+ "already_configured": "Layanan sudah dikonfigurasi",
4
5
  "reauth_successful": "Autentikasi ulang berhasil"
5
6
  },
6
7
  "error": {
@@ -21,6 +22,35 @@
21
22
  }
22
23
  }
23
24
  },
25
+ "config_subentries": {
26
+ "conversation": {
27
+ "abort": {
28
+ "reconfigure_successful": "Konfigurasi ulang berhasil"
29
+ },
30
+ "step": {
31
+ "set_options": {
32
+ "data": {
33
+ "chat_model": "Model",
34
+ "llm_hass_api": "Kendalikan Home Assistant",
35
+ "name": "Nama"
36
+ }
37
+ }
38
+ }
39
+ },
40
+ "tts": {
41
+ "abort": {
42
+ "reconfigure_successful": "Konfigurasi ulang berhasil"
43
+ },
44
+ "step": {
45
+ "set_options": {
46
+ "data": {
47
+ "chat_model": "Model",
48
+ "name": "Nama"
49
+ }
50
+ }
51
+ }
52
+ }
53
+ },
24
54
  "services": {
25
55
  "generate_content": {
26
56
  "description": "Menghasilkan konten dari perintah yang terdiri dari teks dan gambar secara opsional",
@@ -0,0 +1,11 @@
1
+ {
2
+ "config_subentries": {
3
+ "tts": {
4
+ "entry_type": "\u0422\u0435\u043a\u0441\u0442-\u0432\u043e-\u0433\u043e\u0432\u043e\u0440",
5
+ "initiate_flow": {
6
+ "reconfigure": "\u0420\u0435\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0458 \u0458\u0430 \u0443\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0437\u0430 \u043f\u0440\u0435\u0442\u0432\u043e\u0440\u0430\u045a\u0435 \u0442\u0435\u043a\u0441\u0442 \u0432\u043e \u0433\u043e\u0432\u043e\u0440",
7
+ "user": "\u0414\u043e\u0434\u0430\u0458 \u0443\u0441\u043b\u0443\u0433\u0430 \u0437\u0430 \u043f\u0440\u0435\u0442\u0432\u043e\u0440\u0430\u045a\u0435 \u0442\u0435\u043a\u0441\u0442 \u0432\u043e \u0433\u043e\u0432\u043e\u0440"
8
+ }
9
+ }
10
+ }
11
+ }
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "config": {
3
3
  "abort": {
4
+ "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant",
4
5
  "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e"
5
6
  },
6
7
  "error": {
@@ -21,6 +22,72 @@
21
22
  }
22
23
  }
23
24
  },
25
+ "config_subentries": {
26
+ "conversation": {
27
+ "abort": {
28
+ "entry_not_loaded": "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u043f\u043e\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0430.",
29
+ "reconfigure_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e"
30
+ },
31
+ "entry_type": "\u0414\u0438\u0430\u043b\u043e\u0433\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430",
32
+ "error": {
33
+ "invalid_google_search_option": "\u041f\u043e\u0438\u0441\u043a Google \u043c\u043e\u0436\u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \"\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 Home Assistant\" \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043e."
34
+ },
35
+ "initiate_flow": {
36
+ "reconfigure": "\u041f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u0438\u0430\u043b\u043e\u0433\u043e\u0432\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443",
37
+ "user": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u0438\u0430\u043b\u043e\u0433\u043e\u0432\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443"
38
+ },
39
+ "step": {
40
+ "set_options": {
41
+ "data": {
42
+ "chat_model": "\u041c\u043e\u0434\u0435\u043b\u044c",
43
+ "dangerous_block_threshold": "\u041f\u043e\u043e\u0449\u0440\u044f\u0435\u0442, \u0441\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0438\u043b\u0438 \u043f\u043e\u0434\u0441\u0442\u0440\u0435\u043a\u0430\u0435\u0442 \u043a \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044e \u0432\u0440\u0435\u0434\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439",
44
+ "enable_google_search_tool": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u043f\u043e\u0438\u0441\u043a\u0430 Google",
45
+ "harassment_block_threshold": "\u041d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u0438\u043b\u0438 \u0432\u0440\u0435\u0434\u043d\u044b\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438, \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043d\u0430 \u043b\u0438\u0447\u043d\u043e\u0441\u0442\u044c \u0438/\u0438\u043b\u0438 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b",
46
+ "hate_block_threshold": "\u0413\u0440\u0443\u0431\u044b\u0439, \u043d\u0435\u0443\u0432\u0430\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0438\u043b\u0438 \u043d\u0435\u043f\u0440\u0438\u0441\u0442\u043e\u0439\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442",
47
+ "llm_hass_api": "\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 Home Assistant",
48
+ "max_tokens": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u043a\u0435\u043d\u043e\u0432, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0432 \u043e\u0442\u0432\u0435\u0442",
49
+ "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435",
50
+ "prompt": "\u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438",
51
+ "sexual_block_threshold": "\u0421\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u044f \u043e \u0441\u0435\u043a\u0441\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u0445 \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043d\u0435\u043f\u0440\u0438\u0441\u0442\u043e\u0439\u043d\u044b\u0435 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b",
52
+ "temperature": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430",
53
+ "top_k": "Top K",
54
+ "top_p": "Top P"
55
+ },
56
+ "data_description": {
57
+ "enable_google_search_tool": "\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \"\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 Home Assistant\" \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043e. \u0421\u043c. \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043e\u0431\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \"Assist\".",
58
+ "prompt": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435, \u043a\u0430\u043a \u0434\u043e\u043b\u0436\u043d\u0430 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c LLM. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d."
59
+ }
60
+ }
61
+ }
62
+ },
63
+ "tts": {
64
+ "abort": {
65
+ "entry_not_loaded": "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u043f\u043e\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0430.",
66
+ "reconfigure_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e"
67
+ },
68
+ "entry_type": "\u0421\u0438\u043d\u0442\u0435\u0437 \u0440\u0435\u0447\u0438",
69
+ "initiate_flow": {
70
+ "reconfigure": "\u041f\u0435\u0440\u0435\u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u043b\u0443\u0436\u0431\u0443 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0430 \u0432 \u0440\u0435\u0447\u044c",
71
+ "user": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043b\u0443\u0436\u0431\u0443 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0430 \u0432 \u0440\u0435\u0447\u044c"
72
+ },
73
+ "step": {
74
+ "set_options": {
75
+ "data": {
76
+ "chat_model": "\u041c\u043e\u0434\u0435\u043b\u044c",
77
+ "dangerous_block_threshold": "\u041f\u043e\u043e\u0449\u0440\u044f\u0435\u0442, \u0441\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0438\u043b\u0438 \u043f\u043e\u0434\u0441\u0442\u0440\u0435\u043a\u0430\u0435\u0442 \u043a \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044e \u0432\u0440\u0435\u0434\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439",
78
+ "harassment_block_threshold": "\u041d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u0438\u043b\u0438 \u0432\u0440\u0435\u0434\u043d\u044b\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438, \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043d\u0430 \u043b\u0438\u0447\u043d\u043e\u0441\u0442\u044c \u0438/\u0438\u043b\u0438 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b",
79
+ "hate_block_threshold": "\u0413\u0440\u0443\u0431\u044b\u0439, \u043d\u0435\u0443\u0432\u0430\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0438\u043b\u0438 \u043d\u0435\u043f\u0440\u0438\u0441\u0442\u043e\u0439\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442",
80
+ "max_tokens": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u043a\u0435\u043d\u043e\u0432, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0432 \u043e\u0442\u0432\u0435\u0442",
81
+ "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435",
82
+ "sexual_block_threshold": "\u0421\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u044f \u043e \u0441\u0435\u043a\u0441\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u0445 \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043d\u0435\u043f\u0440\u0438\u0441\u0442\u043e\u0439\u043d\u044b\u0435 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b",
83
+ "temperature": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430",
84
+ "top_k": "Top K",
85
+ "top_p": "Top P"
86
+ }
87
+ }
88
+ }
89
+ }
90
+ },
24
91
  "issues": {
25
92
  "deprecated_image_filename_parameter": {
26
93
  "description": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 'image_filename' \u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u0445 Google Generative AI \u0443\u0441\u0442\u0430\u0440\u0435\u043b. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0439\u0442\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c 'filenames' \u0432\u043c\u0435\u0441\u0442\u043e \u043d\u0435\u0433\u043e.",
@@ -60,6 +60,34 @@
60
60
  }
61
61
  }
62
62
  }
63
+ },
64
+ "tts": {
65
+ "abort": {
66
+ "entry_not_loaded": "Nie je mo\u017en\u00e9 prid\u00e1va\u0165 veci, ke\u010f je konfigur\u00e1cia vypnut\u00e1.",
67
+ "reconfigure_successful": "Rekonfigur\u00e1cia bola \u00faspe\u0161n\u00e1"
68
+ },
69
+ "entry_type": "Prevod textu na re\u010d",
70
+ "initiate_flow": {
71
+ "reconfigure": "Rekonfigur\u00e1cia slu\u017eby prevodu textu na re\u010d",
72
+ "user": "Prida\u0165 slu\u017ebu prevodu textu na re\u010d"
73
+ },
74
+ "step": {
75
+ "set_options": {
76
+ "data": {
77
+ "chat_model": "Model",
78
+ "dangerous_block_threshold": "Podporuje, u\u013eah\u010duje alebo nab\u00e1da k \u0161kodliv\u00fdm \u010dinom",
79
+ "harassment_block_threshold": "Negat\u00edvne alebo \u0161kodliv\u00e9 koment\u00e1re zameran\u00e9 na identitu a/alebo chr\u00e1nen\u00e9 vlastnosti",
80
+ "hate_block_threshold": "Obsah, ktor\u00fd je hrub\u00fd, ne\u00factiv\u00fd alebo vulg\u00e1rny",
81
+ "max_tokens": "Maxim\u00e1lny po\u010det tokenov, ktor\u00e9 sa maj\u00fa vr\u00e1ti\u0165 v odpovedi",
82
+ "name": "N\u00e1zov",
83
+ "recommended": "Odpor\u00fa\u010dan\u00e9 nastavenia modelu",
84
+ "sexual_block_threshold": "Obsahuje odkazy na sexu\u00e1lne akty alebo in\u00fd oplzl\u00fd obsah",
85
+ "temperature": "Teplota",
86
+ "top_k": "Top K",
87
+ "top_p": "Top P"
88
+ }
89
+ }
90
+ }
63
91
  }
64
92
  },
65
93
  "issues": {
@@ -40,6 +40,27 @@
40
40
  }
41
41
  }
42
42
  }
43
+ },
44
+ "tts": {
45
+ "abort": {
46
+ "reconfigure_successful": "Omkonfigureringen lyckades"
47
+ },
48
+ "entry_type": "Text-till-tal",
49
+ "initiate_flow": {
50
+ "reconfigure": "Konfigurera om text-till-tal-tj\u00e4nst",
51
+ "user": "L\u00e4gg till text-till-tal-tj\u00e4nst"
52
+ },
53
+ "step": {
54
+ "set_options": {
55
+ "data": {
56
+ "chat_model": "Modell",
57
+ "name": "Namn",
58
+ "recommended": "Rekommenderade modellinst\u00e4llningar",
59
+ "sexual_block_threshold": "Inneh\u00e5ller referenser till sexuella handlingar eller annat oanst\u00e4ndigt inneh\u00e5ll",
60
+ "temperature": "Temperatur"
61
+ }
62
+ }
63
+ }
43
64
  }
44
65
  },
45
66
  "issues": {
@@ -60,6 +60,34 @@
60
60
  }
61
61
  }
62
62
  }
63
+ },
64
+ "tts": {
65
+ "abort": {
66
+ "entry_not_loaded": "\u7981\u7528\u914d\u7f6e\u65f6\u65e0\u6cd5\u6dfb\u52a0\u5185\u5bb9\u3002",
67
+ "reconfigure_successful": "\u91cd\u65b0\u914d\u7f6e\u6210\u529f"
68
+ },
69
+ "entry_type": "\u6587\u672c\u8f6c\u8bed\u97f3",
70
+ "initiate_flow": {
71
+ "reconfigure": "\u91cd\u65b0\u914d\u7f6e\u6587\u672c\u8f6c\u8bed\u97f3\u670d\u52a1",
72
+ "user": "\u6dfb\u52a0\u6587\u672c\u8f6c\u8bed\u97f3\u670d\u52a1"
73
+ },
74
+ "step": {
75
+ "set_options": {
76
+ "data": {
77
+ "chat_model": "\u6a21\u578b",
78
+ "dangerous_block_threshold": "\u4fc3\u8fdb\u3001\u534f\u52a9\u6216\u9f13\u52b1\u6709\u5bb3\u884c\u4e3a",
79
+ "harassment_block_threshold": "\u9488\u5bf9\u8eab\u4efd\u548c/\u6216\u53d7\u4fdd\u62a4\u5c5e\u6027\u7684\u8d1f\u9762\u6216\u6709\u5bb3\u8bc4\u8bba",
80
+ "hate_block_threshold": "\u7c97\u9c81\u3001\u65e0\u793c\u6216\u4eb5\u6e0e\u7684\u5185\u5bb9",
81
+ "max_tokens": "\u56de\u5e94\u4e2d\u8fd4\u56de\u7684\u6700\u5927\u4ee4\u724c\u6570",
82
+ "name": "\u540d\u79f0",
83
+ "recommended": "\u63a8\u8350\u7684\u6a21\u578b\u8bbe\u7f6e",
84
+ "sexual_block_threshold": "\u5305\u542b\u5bf9\u6027\u884c\u4e3a\u6216\u5176\u4ed6\u6deb\u79fd\u5185\u5bb9\u7684\u5f15\u7528",
85
+ "temperature": "\u6e29\u5ea6",
86
+ "top_k": "Top K",
87
+ "top_p": "Top P"
88
+ }
89
+ }
90
+ }
63
91
  }
64
92
  },
65
93
  "issues": {
@@ -60,6 +60,34 @@
60
60
  }
61
61
  }
62
62
  }
63
+ },
64
+ "tts": {
65
+ "abort": {
66
+ "entry_not_loaded": "\u7576\u8a2d\u5b9a\u88ab\u95dc\u9589\u6642\uff0c\u7121\u6cd5\u65b0\u589e\u9805\u76ee\u3002",
67
+ "reconfigure_successful": "\u91cd\u65b0\u8a2d\u5b9a\u6210\u529f"
68
+ },
69
+ "entry_type": "\u6587\u5b57\u8f49\u8a9e\u97f3",
70
+ "initiate_flow": {
71
+ "reconfigure": "\u91cd\u65b0\u8a2d\u5b9a\u6587\u5b57\u8f49\u8a9e\u97f3\u670d\u52d9",
72
+ "user": "\u65b0\u589e\u6587\u5b57\u8f49\u8a9e\u97f3\u670d\u52d9"
73
+ },
74
+ "step": {
75
+ "set_options": {
76
+ "data": {
77
+ "chat_model": "\u578b\u865f",
78
+ "dangerous_block_threshold": "\u4fc3\u9032\u3001\u4fbf\u5229\u6216\u9f13\u52f5\u6709\u5bb3\u884c\u70ba",
79
+ "harassment_block_threshold": "\u91dd\u5c0d\u8eab\u4efd\u53ca/\u6216\u53d7\u4fdd\u8b77\u5c6c\u6027\u7684\u8ca0\u9762\u6216\u6709\u5bb3\u8a55\u8ad6",
80
+ "hate_block_threshold": "\u7c97\u9b6f\u3001\u4e0d\u5c0a\u91cd\u6216\u893b\u7006\u7684\u5167\u5bb9",
81
+ "max_tokens": "\u56de\u61c9\u8fd4\u56de\u6700\u9ad8\u6b0a\u6756\u6578",
82
+ "name": "\u540d\u7a31",
83
+ "recommended": "\u5efa\u8b70\u6a21\u578b\u8a2d\u5b9a",
84
+ "sexual_block_threshold": "\u5305\u542b\u6027\u884c\u70ba\u6216\u5176\u4ed6\u6deb\u7a62\u5167\u5bb9",
85
+ "temperature": "\u6eab\u5ea6",
86
+ "top_k": "Top K",
87
+ "top_p": "Top P"
88
+ }
89
+ }
90
+ }
63
91
  }
64
92
  },
65
93
  "issues": {
@@ -2,13 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from contextlib import suppress
6
- import io
7
- import logging
5
+ from collections.abc import Mapping
8
6
  from typing import Any
9
- import wave
10
7
 
11
8
  from google.genai import types
9
+ from google.genai.errors import APIError, ClientError
10
+ from propcache.api import cached_property
12
11
 
13
12
  from homeassistant.components.tts import (
14
13
  ATTR_VOICE,
@@ -16,15 +15,14 @@ from homeassistant.components.tts import (
16
15
  TtsAudioType,
17
16
  Voice,
18
17
  )
19
- from homeassistant.config_entries import ConfigEntry
18
+ from homeassistant.config_entries import ConfigEntry, ConfigSubentry
20
19
  from homeassistant.core import HomeAssistant, callback
21
20
  from homeassistant.exceptions import HomeAssistantError
22
- from homeassistant.helpers import device_registry as dr
23
21
  from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
24
22
 
25
- from .const import ATTR_MODEL, DOMAIN, RECOMMENDED_TTS_MODEL
26
-
27
- _LOGGER = logging.getLogger(__name__)
23
+ from .const import CONF_CHAT_MODEL, LOGGER, RECOMMENDED_TTS_MODEL
24
+ from .entity import GoogleGenerativeAILLMBaseEntity
25
+ from .helpers import convert_to_wav
28
26
 
29
27
 
30
28
  async def async_setup_entry(
@@ -32,15 +30,23 @@ async def async_setup_entry(
32
30
  config_entry: ConfigEntry,
33
31
  async_add_entities: AddConfigEntryEntitiesCallback,
34
32
  ) -> None:
35
- """Set up TTS entity."""
36
- tts_entity = GoogleGenerativeAITextToSpeechEntity(config_entry)
37
- async_add_entities([tts_entity])
33
+ """Set up TTS entities."""
34
+ for subentry in config_entry.subentries.values():
35
+ if subentry.subentry_type != "tts":
36
+ continue
37
+
38
+ async_add_entities(
39
+ [GoogleGenerativeAITextToSpeechEntity(config_entry, subentry)],
40
+ config_subentry_id=subentry.subentry_id,
41
+ )
38
42
 
39
43
 
40
- class GoogleGenerativeAITextToSpeechEntity(TextToSpeechEntity):
44
+ class GoogleGenerativeAITextToSpeechEntity(
45
+ TextToSpeechEntity, GoogleGenerativeAILLMBaseEntity
46
+ ):
41
47
  """Google Generative AI text-to-speech entity."""
42
48
 
43
- _attr_supported_options = [ATTR_VOICE, ATTR_MODEL]
49
+ _attr_supported_options = [ATTR_VOICE]
44
50
  # See https://ai.google.dev/gemini-api/docs/speech-generation#languages
45
51
  _attr_supported_languages = [
46
52
  "ar-EG",
@@ -68,6 +74,8 @@ class GoogleGenerativeAITextToSpeechEntity(TextToSpeechEntity):
68
74
  "uk-UA",
69
75
  "vi-VN",
70
76
  ]
77
+ # Unused, but required by base class.
78
+ # The Gemini TTS models detect the input language automatically.
71
79
  _attr_default_language = "en-US"
72
80
  # See https://ai.google.dev/gemini-api/docs/speech-generation#voices
73
81
  _supported_voices = [
@@ -106,110 +114,44 @@ class GoogleGenerativeAITextToSpeechEntity(TextToSpeechEntity):
106
114
  )
107
115
  ]
108
116
 
109
- def __init__(self, entry: ConfigEntry) -> None:
110
- """Initialize Google Generative AI Conversation speech entity."""
111
- self.entry = entry
112
- self._attr_name = "Google Generative AI TTS"
113
- self._attr_unique_id = f"{entry.entry_id}_tts"
114
- self._attr_device_info = dr.DeviceInfo(
115
- identifiers={(DOMAIN, entry.entry_id)},
116
- manufacturer="Google",
117
- model="Generative AI",
118
- entry_type=dr.DeviceEntryType.SERVICE,
119
- )
120
- self._genai_client = entry.runtime_data
121
- self._default_voice_id = self._supported_voices[0].voice_id
117
+ def __init__(self, config_entry: ConfigEntry, subentry: ConfigSubentry) -> None:
118
+ """Initialize the TTS entity."""
119
+ super().__init__(config_entry, subentry, RECOMMENDED_TTS_MODEL)
122
120
 
123
121
  @callback
124
- def async_get_supported_voices(self, language: str) -> list[Voice] | None:
122
+ def async_get_supported_voices(self, language: str) -> list[Voice]:
125
123
  """Return a list of supported voices for a language."""
126
124
  return self._supported_voices
127
125
 
126
+ @cached_property
127
+ def default_options(self) -> Mapping[str, Any]:
128
+ """Return a mapping with the default options."""
129
+ return {
130
+ ATTR_VOICE: self._supported_voices[0].voice_id,
131
+ }
132
+
128
133
  async def async_get_tts_audio(
129
134
  self, message: str, language: str, options: dict[str, Any]
130
135
  ) -> TtsAudioType:
131
136
  """Load tts audio file from the engine."""
137
+ config = self.create_generate_content_config()
138
+ config.response_modalities = ["AUDIO"]
139
+ config.speech_config = types.SpeechConfig(
140
+ voice_config=types.VoiceConfig(
141
+ prebuilt_voice_config=types.PrebuiltVoiceConfig(
142
+ voice_name=options[ATTR_VOICE]
143
+ )
144
+ )
145
+ )
132
146
  try:
133
- response = self._genai_client.models.generate_content(
134
- model=options.get(ATTR_MODEL, RECOMMENDED_TTS_MODEL),
147
+ response = await self._genai_client.aio.models.generate_content(
148
+ model=self.subentry.data.get(CONF_CHAT_MODEL, RECOMMENDED_TTS_MODEL),
135
149
  contents=message,
136
- config=types.GenerateContentConfig(
137
- response_modalities=["AUDIO"],
138
- speech_config=types.SpeechConfig(
139
- voice_config=types.VoiceConfig(
140
- prebuilt_voice_config=types.PrebuiltVoiceConfig(
141
- voice_name=options.get(
142
- ATTR_VOICE, self._default_voice_id
143
- )
144
- )
145
- )
146
- ),
147
- ),
150
+ config=config,
148
151
  )
149
-
150
152
  data = response.candidates[0].content.parts[0].inline_data.data
151
153
  mime_type = response.candidates[0].content.parts[0].inline_data.mime_type
152
- except Exception as exc:
153
- _LOGGER.warning(
154
- "Error during processing of TTS request %s", exc, exc_info=True
155
- )
154
+ except (APIError, ClientError, ValueError) as exc:
155
+ LOGGER.error("Error during TTS: %s", exc, exc_info=True)
156
156
  raise HomeAssistantError(exc) from exc
157
- return "wav", self._convert_to_wav(data, mime_type)
158
-
159
- def _convert_to_wav(self, audio_data: bytes, mime_type: str) -> bytes:
160
- """Generate a WAV file header for the given audio data and parameters.
161
-
162
- Args:
163
- audio_data: The raw audio data as a bytes object.
164
- mime_type: Mime type of the audio data.
165
-
166
- Returns:
167
- A bytes object representing the WAV file header.
168
-
169
- """
170
- parameters = self._parse_audio_mime_type(mime_type)
171
-
172
- wav_buffer = io.BytesIO()
173
- with wave.open(wav_buffer, "wb") as wf:
174
- wf.setnchannels(1)
175
- wf.setsampwidth(parameters["bits_per_sample"] // 8)
176
- wf.setframerate(parameters["rate"])
177
- wf.writeframes(audio_data)
178
-
179
- return wav_buffer.getvalue()
180
-
181
- def _parse_audio_mime_type(self, mime_type: str) -> dict[str, int]:
182
- """Parse bits per sample and rate from an audio MIME type string.
183
-
184
- Assumes bits per sample is encoded like "L16" and rate as "rate=xxxxx".
185
-
186
- Args:
187
- mime_type: The audio MIME type string (e.g., "audio/L16;rate=24000").
188
-
189
- Returns:
190
- A dictionary with "bits_per_sample" and "rate" keys. Values will be
191
- integers if found, otherwise None.
192
-
193
- """
194
- if not mime_type.startswith("audio/L"):
195
- _LOGGER.warning("Received unexpected MIME type %s", mime_type)
196
- raise HomeAssistantError(f"Unsupported audio MIME type: {mime_type}")
197
-
198
- bits_per_sample = 16
199
- rate = 24000
200
-
201
- # Extract rate from parameters
202
- parts = mime_type.split(";")
203
- for param in parts: # Skip the main type part
204
- param = param.strip()
205
- if param.lower().startswith("rate="):
206
- # Handle cases like "rate=" with no value or non-integer value and keep rate as default
207
- with suppress(ValueError, IndexError):
208
- rate_str = param.split("=", 1)[1]
209
- rate = int(rate_str)
210
- elif param.startswith("audio/L"):
211
- # Keep bits_per_sample as default if conversion fails
212
- with suppress(ValueError, IndexError):
213
- bits_per_sample = int(param.split("L", 1)[1])
214
-
215
- return {"bits_per_sample": bits_per_sample, "rate": rate}
157
+ return "wav", convert_to_wav(data, mime_type)