appdaemon 4.5.0__tar.gz → 4.5.2__tar.gz

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 (357) hide show
  1. appdaemon-4.5.2/PKG-INFO +80 -0
  2. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/adapi.py +8 -4
  3. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/adbase.py +5 -3
  4. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/app_management.py +15 -6
  5. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/default/variables.yaml +1 -1
  6. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/glassic/variables.yaml +1 -1
  7. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/obsidian/variables.yaml +1 -1
  8. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/simplyred/variables.yaml +1 -1
  9. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/dependency.py +22 -7
  10. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/dependency_manager.py +9 -3
  11. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/entity.py +2 -3
  12. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/exceptions.py +7 -4
  13. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/http.py +16 -12
  14. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/internal/app_management.py +1 -1
  15. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/hass/hassapi.py +2 -2
  16. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/hass/hassplugin.py +2 -2
  17. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/mqtt/mqttapi.py +7 -13
  18. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/scheduler.py +74 -215
  19. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/threads.py +30 -59
  20. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/utils.py +19 -20
  21. appdaemon-4.5.2/appdaemon/version.py +2 -0
  22. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basefan/basefan.js +1 -1
  23. appdaemon-4.5.2/appdaemon.egg-info/PKG-INFO +80 -0
  24. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon.egg-info/requires.txt +1 -0
  25. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/ObjectTracker/general_app_functions.py +4 -8
  26. {appdaemon-4.5.0 → appdaemon-4.5.2}/pyproject.toml +11 -4
  27. appdaemon-4.5.0/PKG-INFO +0 -275
  28. appdaemon-4.5.0/appdaemon/version.py +0 -2
  29. appdaemon-4.5.0/appdaemon.egg-info/PKG-INFO +0 -275
  30. {appdaemon-4.5.0 → appdaemon-4.5.2}/LICENSE.md +0 -0
  31. {appdaemon-4.5.0 → appdaemon-4.5.2}/MANIFEST.in +0 -0
  32. {appdaemon-4.5.0 → appdaemon-4.5.2}/README.md +0 -0
  33. {appdaemon-4.5.0 → appdaemon-4.5.2}/README.rst +0 -0
  34. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/__init__.py +0 -0
  35. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/__main__.py +0 -0
  36. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/admin.py +0 -0
  37. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/admin_loop.py +0 -0
  38. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/appdaemon.py +0 -0
  39. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/aui/appdaemon.png +0 -0
  40. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/aui/css/app.css +0 -0
  41. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/aui/css/chunk-vendors.css +0 -0
  42. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/aui/favicon.ico +0 -0
  43. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/aui/index.html +0 -0
  44. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/aui/js/app.js +0 -0
  45. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/aui/js/app.js.map +0 -0
  46. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/aui/js/chunk-vendors.js +0 -0
  47. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/aui/js/chunk-vendors.js.map +0 -0
  48. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/all.css +0 -0
  49. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/climacons-font.css +0 -0
  50. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/default/dashboard.css +0 -0
  51. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/fontawesome.min.css +0 -0
  52. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/fonts.css +0 -0
  53. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/glassic/dashboard.css +0 -0
  54. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/glassic/img/carbon1.jpg +0 -0
  55. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/glassic/img/carbon2.jpg +0 -0
  56. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/glassic/img/carbon3.jpg +0 -0
  57. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/glassic/img/carbon4.jpg +0 -0
  58. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/glassic/img/glassic_bg.jpg +0 -0
  59. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/images/ui-icons_444444_256x240.png +0 -0
  60. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/images/ui-icons_555555_256x240.png +0 -0
  61. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/images/ui-icons_777620_256x240.png +0 -0
  62. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/images/ui-icons_777777_256x240.png +0 -0
  63. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/images/ui-icons_cc0000_256x240.png +0 -0
  64. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/images/ui-icons_ffffff_256x240.png +0 -0
  65. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/jquery-ui.css +0 -0
  66. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/jquery.gridster.css +0 -0
  67. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/main.css +0 -0
  68. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/materialdesignicons.min.css +0 -0
  69. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/non_dashboard.css +0 -0
  70. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/obsidian/dashboard.css +0 -0
  71. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/obsidian/img/obsidian_bg.jpg +0 -0
  72. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/obsidian/img/obsidian_w_bg.jpg +0 -0
  73. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/obsidian/img/obsidianbg.jpg +0 -0
  74. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/obsidian/img/widgetbg.jpg +0 -0
  75. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/rickshaw.min.css +0 -0
  76. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/simplyred/dashboard.css +0 -0
  77. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/simplyred/img/goldgradient.png +0 -0
  78. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/simplyred/img/goldtexture.jpg +0 -0
  79. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/simplyred/js/timer.js +0 -0
  80. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/v4-shims.min.css +0 -0
  81. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/zen/dashboard.css +0 -0
  82. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/zen/img/zen_bg.jpg +0 -0
  83. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/zen/img/zen_bg2.jpg +0 -0
  84. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/zen/img/zen_weatherbg.jpg +0 -0
  85. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/css/zen/variables.yaml +0 -0
  86. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/TickingTimebombBB.ttf +0 -0
  87. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/climacons-webfont.eot +0 -0
  88. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/climacons-webfont.svg +0 -0
  89. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/climacons-webfont.ttf +0 -0
  90. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/climacons-webfont.woff +0 -0
  91. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/digital-7-mono.eot +0 -0
  92. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/digital-7-mono.ttf +0 -0
  93. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/materialdesignicons-webfont.eot +0 -0
  94. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/materialdesignicons-webfont.ttf +0 -0
  95. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/materialdesignicons-webfont.woff +0 -0
  96. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/materialdesignicons-webfont.woff2 +0 -0
  97. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/fonts/repetition.ttf +0 -0
  98. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/Blank.gif +0 -0
  99. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/android-chrome-192x192.png +0 -0
  100. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/android-chrome-512x512.png +0 -0
  101. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/apple-touch-icon.png +0 -0
  102. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/browserconfig.xml +0 -0
  103. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/favicon-16x16.png +0 -0
  104. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/favicon-32x32.png +0 -0
  105. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/favicon.ico +0 -0
  106. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/manifest.json +0 -0
  107. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/mstile-144x144.png +0 -0
  108. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/mstile-150x150.png +0 -0
  109. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/mstile-310x150.png +0 -0
  110. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/mstile-310x310.png +0 -0
  111. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/images/mstile-70x70.png +0 -0
  112. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/admin.js +0 -0
  113. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/d3.layout.min.js +0 -0
  114. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/d3.min.js +0 -0
  115. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/dashboard.js +0 -0
  116. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/gauge.min.js +0 -0
  117. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/jquery-3.1.1.min.js +0 -0
  118. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/jquery-ui.js +0 -0
  119. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/jquery.gridster.js +0 -0
  120. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/justgage.js +0 -0
  121. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/knockout-3.4.1.js +0 -0
  122. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/list.min.js +0 -0
  123. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/plotly.min.js +0 -0
  124. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/raphael-2.1.4.min.js +0 -0
  125. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/reconnecting-websocket.min.js +0 -0
  126. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/rickshaw.min.js +0 -0
  127. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/socket.io.js +0 -0
  128. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/sockjs.min.js +0 -0
  129. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/javascript/stream.js +0 -0
  130. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/templates/admin.jinja2 +0 -0
  131. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/templates/body_include.jinja2 +0 -0
  132. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/templates/dashboard.jinja2 +0 -0
  133. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/templates/dashinit.jinja2 +0 -0
  134. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/templates/error.jinja2 +0 -0
  135. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/templates/head_include.jinja2 +0 -0
  136. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/templates/list.jinja2 +0 -0
  137. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/templates/logon.jinja2 +0 -0
  138. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-brands-400.eot +0 -0
  139. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-brands-400.svg +0 -0
  140. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-brands-400.ttf +0 -0
  141. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-brands-400.woff +0 -0
  142. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-brands-400.woff2 +0 -0
  143. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-regular-400.eot +0 -0
  144. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-regular-400.svg +0 -0
  145. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-regular-400.ttf +0 -0
  146. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-regular-400.woff +0 -0
  147. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-regular-400.woff2 +0 -0
  148. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-solid-900.eot +0 -0
  149. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-solid-900.svg +0 -0
  150. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-solid-900.ttf +0 -0
  151. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-solid-900.woff +0 -0
  152. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/assets/webfonts/fa-solid-900.woff2 +0 -0
  153. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/callbacks.py +0 -0
  154. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/dashboard.py +0 -0
  155. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/events.py +0 -0
  156. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/futures.py +0 -0
  157. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/logging.py +0 -0
  158. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/__init__.py +0 -0
  159. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/__init__.py +0 -0
  160. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/api.py +0 -0
  161. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/app.py +0 -0
  162. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/appdaemon.py +0 -0
  163. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/common.py +0 -0
  164. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/dashboard.py +0 -0
  165. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/http.py +0 -0
  166. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/log.py +0 -0
  167. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/misc.py +0 -0
  168. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/plugin.py +0 -0
  169. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/sequence.py +0 -0
  170. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/config/yaml.py +0 -0
  171. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/internal/__init__.py +0 -0
  172. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/internal/file_check.py +0 -0
  173. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/internal/plugin.py +0 -0
  174. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/internal/state.py +0 -0
  175. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/internal/threading.py +0 -0
  176. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/notification/__init__.py +0 -0
  177. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/notification/android.py +0 -0
  178. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/notification/base.py +0 -0
  179. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/models/notification/iOS.py +0 -0
  180. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugin_management.py +0 -0
  181. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/__init__.py +0 -0
  182. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/dummy/dummyapi.py +0 -0
  183. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/dummy/dummyplugin.py +0 -0
  184. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/hass/__init__.py +0 -0
  185. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/hass/exceptions.py +0 -0
  186. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/hass/models.py +0 -0
  187. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/hass/notifications.py +0 -0
  188. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/hass/utils.py +0 -0
  189. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/mqtt/__init__.py +0 -0
  190. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/plugins/mqtt/mqttplugin.py +0 -0
  191. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/pyversions.py +0 -0
  192. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/sequences.py +0 -0
  193. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/services.py +0 -0
  194. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/state.py +0 -0
  195. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/stream/__init__.py +0 -0
  196. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/stream/adstream.py +0 -0
  197. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/stream/socketio_handler.py +0 -0
  198. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/stream/sockjs_handler.py +0 -0
  199. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/stream/ws_handler.py +0 -0
  200. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/thread_async.py +0 -0
  201. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/utility_loop.py +0 -0
  202. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/AdminLog.yaml +0 -0
  203. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/AdminSummary.yaml +0 -0
  204. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/AdminTable.yaml +0 -0
  205. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/alarm.yaml +0 -0
  206. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basealarm/basealarm.css +0 -0
  207. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basealarm/basealarm.html +0 -0
  208. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basealarm/basealarm.js +0 -0
  209. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basecamera/basecamera.css +0 -0
  210. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basecamera/basecamera.html +0 -0
  211. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basecamera/basecamera.js +0 -0
  212. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseclimate/baseclimate.css +0 -0
  213. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseclimate/baseclimate.html +0 -0
  214. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseclimate/baseclimate.js +0 -0
  215. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseclock/baseclock.css +0 -0
  216. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseclock/baseclock.html +0 -0
  217. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseclock/baseclock.js +0 -0
  218. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basedatetime/basedatetime.css +0 -0
  219. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basedatetime/basedatetime.html +0 -0
  220. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basedatetime/basedatetime.js +0 -0
  221. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basedisplay/basedisplay.css +0 -0
  222. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basedisplay/basedisplay.html +0 -0
  223. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basedisplay/basedisplay.js +0 -0
  224. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseentitypicture/baseentitypicture.css +0 -0
  225. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseentitypicture/baseentitypicture.html +0 -0
  226. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseentitypicture/baseentitypicture.js +0 -0
  227. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseerror/baseerror.css +0 -0
  228. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseerror/baseerror.html +0 -0
  229. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseerror/baseerror.js +0 -0
  230. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basefan/basefan.css +0 -0
  231. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basefan/basefan.html +0 -0
  232. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basegauge/basegauge.css +0 -0
  233. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basegauge/basegauge.html +0 -0
  234. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basegauge/basegauge.js +0 -0
  235. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseheater/baseheater.css +0 -0
  236. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseheater/baseheater.html +0 -0
  237. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseheater/baseheater.js +0 -0
  238. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseicon/baseicon.css +0 -0
  239. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseicon/baseicon.html +0 -0
  240. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseicon/baseicon.js +0 -0
  241. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseiframe/baseiframe.css +0 -0
  242. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseiframe/baseiframe.html +0 -0
  243. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseiframe/baseiframe.js +0 -0
  244. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseinputnumber/baseinputnumber.css +0 -0
  245. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseinputnumber/baseinputnumber.html +0 -0
  246. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseinputnumber/baseinputnumber.js +0 -0
  247. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basejavascript/basejavascript.css +0 -0
  248. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basejavascript/basejavascript.html +0 -0
  249. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basejavascript/basejavascript.js +0 -0
  250. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baselight/baselight.css +0 -0
  251. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baselight/baselight.html +0 -0
  252. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baselight/baselight.js +0 -0
  253. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basemedia/basemedia.css +0 -0
  254. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basemedia/basemedia.html +0 -0
  255. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basemedia/basemedia.js +0 -0
  256. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseradial/baseradial.css +0 -0
  257. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseradial/baseradial.html +0 -0
  258. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseradial/baseradial.js +0 -0
  259. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baserss/baserss.css +0 -0
  260. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baserss/baserss.html +0 -0
  261. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baserss/baserss.js +0 -0
  262. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseselect/baseselect.css +0 -0
  263. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseselect/baseselect.html +0 -0
  264. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseselect/baseselect.js +0 -0
  265. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseslider/baseslider.css +0 -0
  266. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseslider/baseslider.html +0 -0
  267. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseslider/baseslider.js +0 -0
  268. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseswitch/baseswitch.css +0 -0
  269. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseswitch/baseswitch.html +0 -0
  270. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseswitch/baseswitch.js +0 -0
  271. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basetemperature/basetemperature.css +0 -0
  272. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basetemperature/basetemperature.html +0 -0
  273. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basetemperature/basetemperature.js +0 -0
  274. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basetext/basetext.css +0 -0
  275. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basetext/basetext.html +0 -0
  276. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/basetext/basetext.js +0 -0
  277. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseweather/baseweather.css +0 -0
  278. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseweather/baseweather.html +0 -0
  279. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/baseweather/baseweather.js +0 -0
  280. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/binary_sensor.yaml +0 -0
  281. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/camera.yaml +0 -0
  282. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/climate.yaml +0 -0
  283. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/clock.yaml +0 -0
  284. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/cover.yaml +0 -0
  285. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/device_tracker.yaml +0 -0
  286. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/entitypicture.yaml +0 -0
  287. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/fan.yaml +0 -0
  288. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/gauge.yaml +0 -0
  289. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/group.yaml +0 -0
  290. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/heater.yaml +0 -0
  291. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/icon.yaml +0 -0
  292. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/iframe.yaml +0 -0
  293. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/input_boolean.yaml +0 -0
  294. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/input_datetime.yaml +0 -0
  295. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/input_number.yaml +0 -0
  296. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/input_select.yaml +0 -0
  297. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/input_slider.yaml +0 -0
  298. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/input_text.yaml +0 -0
  299. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/javascript.yaml +0 -0
  300. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/label.yaml +0 -0
  301. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/light.yaml +0 -0
  302. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/lock.yaml +0 -0
  303. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/london_underground.yaml +0 -0
  304. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/media_player.yaml +0 -0
  305. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/mode.yaml +0 -0
  306. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/navigate.yaml +0 -0
  307. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/person.yaml +0 -0
  308. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/pirateweather.yaml +0 -0
  309. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/radial.yaml +0 -0
  310. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/reload.yaml +0 -0
  311. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/rss.yaml +0 -0
  312. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/scene.yaml +0 -0
  313. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/script.yaml +0 -0
  314. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/sensor.yaml +0 -0
  315. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/sequence.yaml +0 -0
  316. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/switch.yaml +0 -0
  317. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/temperature.yaml +0 -0
  318. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/text_sensor.yaml +0 -0
  319. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/weather.yaml +0 -0
  320. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon/widgets/weather_summary.yaml +0 -0
  321. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon.egg-info/SOURCES.txt +0 -0
  322. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon.egg-info/dependency_links.txt +0 -0
  323. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon.egg-info/entry_points.txt +0 -0
  324. {appdaemon-4.5.0 → appdaemon-4.5.2}/appdaemon.egg-info/top_level.txt +0 -0
  325. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/apps/hello.py +0 -0
  326. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/ObjectTracker/objectcontrole.py +0 -0
  327. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/alexa.py +0 -0
  328. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/battery.py +0 -0
  329. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/bysykkel.py +0 -0
  330. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/commute.py +0 -0
  331. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/dark_day.py +0 -0
  332. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/door_notification.py +0 -0
  333. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/eventCache.py +0 -0
  334. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/eventMonitor.py +0 -0
  335. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/globals.py +0 -0
  336. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/grandfather.py +0 -0
  337. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/hwcheck.py +0 -0
  338. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/ical.py +0 -0
  339. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/minimote.py +0 -0
  340. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/modes.py +0 -0
  341. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/momentary_switch.py +0 -0
  342. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/motion_lights.py +0 -0
  343. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/motion_notification.py +0 -0
  344. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/occusim.py +0 -0
  345. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/outside_lights.py +0 -0
  346. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/presence.py +0 -0
  347. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/ruter.py +0 -0
  348. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/secure.py +0 -0
  349. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/sensor_notification.py +0 -0
  350. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/sensor_notify.py +0 -0
  351. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/sequence.py +0 -0
  352. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/smart_heat.py +0 -0
  353. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/sound.py +0 -0
  354. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/switch_reset.py +0 -0
  355. {appdaemon-4.5.0 → appdaemon-4.5.2}/conf/example_apps/yr.py +0 -0
  356. {appdaemon-4.5.0 → appdaemon-4.5.2}/setup.cfg +0 -0
  357. {appdaemon-4.5.0 → appdaemon-4.5.2}/tests/test_main.py +0 -0
@@ -0,0 +1,80 @@
1
+ Metadata-Version: 2.4
2
+ Name: appdaemon
3
+ Version: 4.5.2
4
+ Summary: Apps for the Home Assistant home automation package.
5
+ Author-email: Andrew I Cockburn <appdaemon@acockburn.com>
6
+ Project-URL: documentation, https://appdaemon.readthedocs.io
7
+ Project-URL: repository, https://github.com/AppDaemon
8
+ Project-URL: changelog, https://github.com/AppDaemon/appdaemon/blob/master/docs/HISTORY.rst
9
+ Keywords: appdaemon,home,automation
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Natural Language :: English
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Home Automation
17
+ Requires-Python: <3.13,>=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE.md
20
+ Requires-Dist: importlib-metadata; python_version < "3.8"
21
+ Requires-Dist: aiohttp<3.12.0,>=3.9.0
22
+ Requires-Dist: aiohttp_jinja2<1.7.0,>=1.5.1
23
+ Requires-Dist: astral~=3.2
24
+ Requires-Dist: bcrypt<4.3.0,>=4.0.1
25
+ Requires-Dist: deepdiff==8.2.0
26
+ Requires-Dist: feedparser~=6.0.10
27
+ Requires-Dist: iso8601<2.2,>=1.1
28
+ Requires-Dist: paho-mqtt<2.2.0,>=1.6.1
29
+ Requires-Dist: pid~=3.0.4
30
+ Requires-Dist: python-dateutil<2.10.0,>=2.8.2
31
+ Requires-Dist: python-socketio<5.12,>=5.5
32
+ Requires-Dist: pytz<2025.2.0,>=2022.7.1
33
+ Requires-Dist: pyyaml~=6.0.1
34
+ Requires-Dist: requests<2.33.0,>=2.28.2
35
+ Requires-Dist: sockjs<0.14,>=0.11
36
+ Requires-Dist: uvloop==0.21.0; sys_platform != "win32"
37
+ Requires-Dist: tomli==2.2.1
38
+ Requires-Dist: tomli_w<1.3,>=1.0
39
+ Requires-Dist: pydantic~=2.10.6
40
+ Provides-Extra: dev
41
+ Requires-Dist: pip-tools<7.5.0,>=6.12.3; extra == "dev"
42
+ Requires-Dist: build<1.3,>=0.10; extra == "dev"
43
+ Requires-Dist: flake8~=7.1.1; extra == "dev"
44
+ Requires-Dist: black<25.2,>=23.1; extra == "dev"
45
+ Requires-Dist: pre-commit<3.9.0,>=3.1.1; python_version > "3.7" and extra == "dev"
46
+ Requires-Dist: pytest<8.4.0,>=7.2.1; extra == "dev"
47
+ Requires-Dist: ruff<0.7.0,>=0.0.261; extra == "dev"
48
+ Requires-Dist: codespell>=2.4.1; extra == "dev"
49
+ Provides-Extra: doc
50
+ Requires-Dist: sphinx-autobuild<2024.10.0,>=2021.3.14; extra == "doc"
51
+ Requires-Dist: sphinx-rtd-theme==3.0.2; extra == "doc"
52
+ Requires-Dist: pygments<2.20,>=2.14; extra == "doc"
53
+ Requires-Dist: myst-parser==4.0.0; extra == "doc"
54
+ Dynamic: license-file
55
+
56
+ # Description
57
+
58
+ AppDaemon is a loosely coupled, multi-threaded, sandboxed python
59
+ execution environment for writing automation apps for various types of Home Automation Software including [Home
60
+ Assistant](https://home-assistant.io/) and MQTT. It has a pluggable architecture allowing it to be integrated with
61
+ practically any event driven application.
62
+
63
+ It also provides a configurable dashboard (HADashboard)
64
+ suitable for wall mounted tablets.
65
+
66
+ ## Getting started
67
+
68
+ For full instructions on installation and usage check out the [AppDaemon Project Documentation](http://appdaemon.readthedocs.io).
69
+
70
+ ## Release Cycle Frequency
71
+
72
+ AppDaemon has reached a very stable point, works reliably and is fairly feature rich at this point
73
+ in its development. For that reason, releases have been slow in recent months. This does not mean that AppDaemon has been abandoned -
74
+ it is used every day by the core developers and has an active discord server [here](https://discord.gg/sgSr79jW5x) - please join us for tips
75
+ and tricks, AppDaemon discussions and general home automation.
76
+
77
+ ## Contributing
78
+
79
+ This is an active open-source project. We are always open to people who want to use the code or contribute to it. Thank you for being involved!
80
+ Have a look at the [official documentation](https://appdaemon.readthedocs.io/en/latest/DEV.html) for more information.
@@ -255,8 +255,11 @@ class ADAPI:
255
255
  # Its a user defined log
256
256
  logger = self.logger if log is None else self.get_user_log(log)
257
257
 
258
+ if ascii_encode is None:
259
+ ascii_encode = self.AD.config.ascii_encode
260
+
258
261
  kwargs = dict(
259
- ascii_encode=ascii_encode or self.AD.config.ascii_encode,
262
+ ascii_encode=ascii_encode,
260
263
  stack_info=stack_info,
261
264
  stacklevel=stacklevel,
262
265
  extra=extra,
@@ -1678,7 +1681,7 @@ class ADAPI:
1678
1681
  `namespace documentation <APPGUIDE.html#namespaces>`__ for more information.
1679
1682
  copy (bool, optional): Whether to return a copy of the internal data. This is ``True`` by default in order
1680
1683
  to protect the user from accidentally modifying AppDaemon's internal data structures, which is dangerous
1681
- and can cause undefined behvaior. Only set this to ``False`` for read-only operations.
1684
+ and can cause undefined behavior. Only set this to ``False`` for read-only operations.
1682
1685
 
1683
1686
  Returns:
1684
1687
  The state or attribute of the entity ID provided or a dict of that maps entity IDs to their respective
@@ -1809,7 +1812,7 @@ class ADAPI:
1809
1812
  accept arbitrary keyword arguments.
1810
1813
 
1811
1814
  Registering services in namespaces that already have plugins is not recommended, as it can lead to some
1812
- unpredictable behavior. Intead, it's recommended to use a user-defined namespace or one that is not tied to
1815
+ unpredictable behavior. Instead, it's recommended to use a user-defined namespace or one that is not tied to
1813
1816
  plugin.
1814
1817
 
1815
1818
  Args:
@@ -3639,7 +3642,8 @@ class ADAPI:
3639
3642
  self.AD.futures.add_future(self.name, future)
3640
3643
  return future
3641
3644
 
3642
- def create_task(
3645
+ @utils.sync_decorator
3646
+ async def create_task(
3643
3647
  self,
3644
3648
  coro: Coroutine[Any, Any, T],
3645
3649
  callback: Callable | None = None,
@@ -5,8 +5,9 @@ from pathlib import Path
5
5
  from typing import TYPE_CHECKING
6
6
 
7
7
  from appdaemon import adapi
8
+ from appdaemon import utils
8
9
  from appdaemon.models.config.app import AppConfig
9
- from appdaemon.utils import StateAttrs
10
+
10
11
 
11
12
  # Check if the module is being imported using the legacy method
12
13
  if __name__ == Path(__file__).name:
@@ -31,7 +32,7 @@ class Entities: # @todo
31
32
  pass
32
33
 
33
34
  def __get__(self, instance, owner):
34
- stateattrs = StateAttrs(instance.get_state())
35
+ stateattrs = utils.StateAttrs(instance.get_state())
35
36
  return stateattrs
36
37
 
37
38
 
@@ -165,7 +166,8 @@ class ADBase:
165
166
  def get_ad_api(self) -> adapi.ADAPI:
166
167
  return adapi.ADAPI(self.AD, self.config_model)
167
168
 
168
- def get_plugin_api(self, plugin_name: str):
169
+ @utils.sync_decorator
170
+ async def get_plugin_api(self, plugin_name: str):
169
171
  """Get the plugin API for a specific plugin."""
170
172
  if isinstance(cfg := self.app_config.root.get(self.name), AppConfig):
171
173
  return self.AD.plugins.get_plugin_api(plugin_name, cfg)
@@ -569,7 +569,7 @@ class AppManagement:
569
569
  yield new_cfg
570
570
 
571
571
  def update(d1: dict, d2: dict) -> dict:
572
- """Internal funciton to log warnings if an app's name gets repeated."""
572
+ """Internal function to log warnings if an app's name gets repeated."""
573
573
  if overlap := set(k.lower() for k in d2 if k in d1):
574
574
  # There's a special case for the sequences in order to merge them if they're defined in multiple files
575
575
  if "sequence" in overlap:
@@ -615,7 +615,7 @@ class AppManagement:
615
615
  self._compare_sequences(update_actions, cfg, files_to_read)
616
616
  continue
617
617
 
618
- if name in self.non_apps:
618
+ if name in self.non_apps or cfg.disable:
619
619
  continue
620
620
 
621
621
  # New config found
@@ -685,8 +685,14 @@ class AppManagement:
685
685
  if not module_name.startswith("appdaemon"):
686
686
  self.logger.debug("Importing '%s'", module_name)
687
687
  importlib.import_module(module_name)
688
- except SyntaxError as exc:
689
- path = Path(exc.filename)
688
+ except Exception as exc:
689
+ match exc:
690
+ case SyntaxError():
691
+ path = Path(exc.filename)
692
+ case NameError():
693
+ path = Path(traceback.extract_tb(exc.__traceback__)[-1].filename)
694
+ case _:
695
+ raise exc
690
696
  mtime = self.dependency_manager.python_deps.files.mtimes.get(path)
691
697
  self.dependency_manager.python_deps.bad_files.add((path, mtime))
692
698
  raise exc
@@ -846,7 +852,7 @@ class AppManagement:
846
852
  for path in import_dirs:
847
853
  self.add_to_import_path(path)
848
854
 
849
- # Add any aditional import paths
855
+ # Add any additional import paths
850
856
  for path in map(Path, self.AD.import_paths):
851
857
  if not path.exists():
852
858
  self.logger.warning(
@@ -989,13 +995,16 @@ class AppManagement:
989
995
  update_actions.apps.reload -= failed_to_stop
990
996
 
991
997
  async def _start_apps(self, update_actions: UpdateActions):
998
+ if failed := update_actions.apps.failed:
999
+ self.logger.warning('Failed to start apps: %s', failed)
1000
+
992
1001
  start_order = update_actions.apps.start_sort(self.dependency_manager)
993
1002
  if start_order:
994
1003
  self.logger.info("Starting apps: %s", update_actions.apps.init_set)
995
1004
  self.logger.debug("App start order: %s", start_order)
996
1005
 
997
1006
  for app_name in start_order:
998
- if isinstance((cfg := self.app_config.root[app_name]), AppConfig):
1007
+ if isinstance((cfg := self.app_config.root[app_name]), AppConfig) and not cfg.disable:
999
1008
  @ade.wrap_async(self.error, self.AD.app_dir, f"'{app_name}' instantiation")
1000
1009
  async def safe_create(self: "AppManagement"):
1001
1010
  try:
@@ -1,6 +1,6 @@
1
1
  # Core styles. Reference these wherever possible rather than making custom
2
2
  # versions of red, green, etc. This will make it easier to quickly change the
3
- # overall pallette of the dashboard just by overriding these variables.
3
+ # overall palette of the dashboard just by overriding these variables.
4
4
 
5
5
  # This file contains the application "defaults".
6
6
  # IF YOU WANT TO CUSTOMIZE styleS FOR YOUR OWN INSTANCE, DO NOT EDIT THIS FILE.
@@ -1,6 +1,6 @@
1
1
  # Core styles. Reference these wherever possible rather than making custom
2
2
  # versions of red, green, etc. This will make it easier to quickly change the
3
- # overall pallette of the dashboard just by overriding these variables.
3
+ # overall palette of the dashboard just by overriding these variables.
4
4
 
5
5
  # This file contains the application "defaults".
6
6
  # IF YOU WANT TO CUSTOMIZE styleS FOR YOUR OWN INSTANCE, DO NOT EDIT THIS FILE.
@@ -22,7 +22,7 @@
22
22
  # gray and getting progressively darker towards night. Be aware that if you remove
23
23
  # it, you will end up with a white background so plan accordingly if this is a problem.
24
24
  #
25
- # The meta tag jsut above this refreshes your dash once an hour so you can see the
25
+ # The meta tag just above this refreshes your dash once an hour so you can see the
26
26
  # effect of the script without manually reloading the dashboard. The content value
27
27
  # is in seconds so if you want it shorter or longer you can adjust that value
28
28
  #
@@ -22,7 +22,7 @@
22
22
  # gray and getting progressively darker towards night. Be aware that if you remove
23
23
  # it, you will end up with a white background so plan accordingly if this is a problem.
24
24
  #
25
- # The meta tag jsut above this refreshes your dash once an hour so you can see the
25
+ # The meta tag just above this refreshes your dash once an hour so you can see the
26
26
  # effect of the script without manually reloading the dashboard. The content value
27
27
  # is in seconds so if you want it shorter or longer you can adjust that value
28
28
  #
@@ -92,6 +92,7 @@ def get_file_deps(file_path: str | Path) -> set[str]:
92
92
  mod: ast.Module = ast.parse(file_content, filename=file_path)
93
93
  except Exception as e:
94
94
  logger.warning(f"Error parsing python module with AST: {e}")
95
+ raise e
95
96
  else:
96
97
  for node in get_imports(mod):
97
98
  match node:
@@ -107,18 +108,32 @@ def get_file_deps(file_path: str | Path) -> set[str]:
107
108
  return set(gen_modules())
108
109
 
109
110
 
110
- def get_dependency_graph(files: Iterable[Path], exclude: set[Path] | None = None):
111
- graph = {
112
- get_full_module_name(f): get_file_deps(f)
113
- for f in files
114
- if exclude is None or f not in exclude
115
- }
111
+ def get_dependency_graph(
112
+ files: Iterable[Path],
113
+ exclude: set[Path] | None = None
114
+ ) -> tuple[dict[str, set[str]], set[Path]]:
115
+ """Gets the dependency graph for some Python files.
116
+
117
+ Returns:
118
+ A tuple containing:
119
+ - A dictionary where keys are module names and values are sets of module names that the key module depends on.
120
+ - A set of paths that failed to parse or resolve dependencies.
121
+ """
122
+ graph = {}
123
+ failed = set()
124
+ for f in files:
125
+ if exclude is None or f not in exclude:
126
+ try:
127
+ graph[get_full_module_name(f)] = get_file_deps(f)
128
+ except Exception:
129
+ failed.add(f)
130
+ continue
116
131
 
117
132
  for mod, deps in graph.items():
118
133
  if mod in deps:
119
134
  deps.remove(mod)
120
135
 
121
- return graph
136
+ return graph, failed
122
137
 
123
138
 
124
139
  def get_all_nodes(deps: dict[str, set[str]]) -> set[str]:
@@ -1,4 +1,5 @@
1
1
  from abc import ABC
2
+ from copy import deepcopy
2
3
  from dataclasses import InitVar, dataclass, field
3
4
  from pathlib import Path
4
5
  from typing import Iterable
@@ -16,14 +17,14 @@ class Dependencies(ABC):
16
17
  ext: str = field(init=False) # this has to be defined by the children classes
17
18
  dep_graph: dict[str, set[str]] = field(init=False)
18
19
  rev_graph: dict[str, set[str]] = field(init=False)
19
- bad_files: set[Path] = field(default_factory=set, init=False)
20
+ bad_files: set[tuple[Path, float]] = field(default_factory=set, init=False)
20
21
 
21
22
  def __post_init__(self):
22
23
  self.refresh_dep_graph()
23
24
 
24
25
  def update(self, new_files: Iterable[Path]):
25
26
  self.files.update(new_files)
26
- for bf, mtime in self.bad_files:
27
+ for bf, mtime in deepcopy(self.bad_files):
27
28
  new_mtime = self.files.mtimes.get(bf)
28
29
  if new_mtime != mtime:
29
30
  assert new_mtime > mtime, f"File {bf} was modified in the future"
@@ -65,7 +66,12 @@ class PythonDeps(Dependencies):
65
66
  if self.bad_files:
66
67
  bad_files, _ = zip(*self.bad_files)
67
68
  bad_files = set(bad_files)
68
- self.dep_graph = get_dependency_graph(self.files, exclude=bad_files)
69
+
70
+ self.dep_graph, failed = get_dependency_graph(self.files, exclude=bad_files)
71
+
72
+ for file in failed:
73
+ self.bad_files.add((file, self.files.mtimes.get(file, 0)))
74
+
69
75
  self.rev_graph = reverse_graph(self.dep_graph)
70
76
 
71
77
  def modules_to_import(self) -> set[str]:
@@ -151,7 +151,7 @@ class Entity:
151
151
  default (any, optional): The value to return when the entity or the attribute doesn't exist.
152
152
  copy (bool, optional): Whether to return a copy of the internal data. This is ``True`` by default in order
153
153
  to protect the user from accidentally modifying AppDaemon's internal data structures, which is dangerous
154
- and can cause undefined behvaior. Only set this to ``False`` for read-only operations.
154
+ and can cause undefined behavior. Only set this to ``False`` for read-only operations.
155
155
 
156
156
  Returns:
157
157
  The entire state of the entity at that given time, if if ``get_state()``
@@ -550,7 +550,6 @@ class Entity:
550
550
 
551
551
  @property
552
552
  def _simple_state(self) -> dict[str, Any]:
553
- return self.adapi.get_state()
554
553
  return self.AD.state.get_state_simple(self.namespace, self.entity_id)
555
554
 
556
555
  @property
@@ -603,7 +602,7 @@ class Entity:
603
602
  def last_changed_delta(self) -> timedelta | None:
604
603
  """A timedelta object representing the time since the entity was last changed"""
605
604
  if time_str := self.last_changed:
606
- compare = self.adapi.parse_time(time_str, aware=True)
605
+ compare = self.adapi.parse_datetime(time_str, aware=True)
607
606
  now = self.AD.sched.get_now_sync()
608
607
  return (now - compare)
609
608
 
@@ -54,9 +54,10 @@ def user_exception_block(logger: Logger, exception: AppDaemonException, app_dir:
54
54
  """Function to generate a user-friendly block of text for an exception. Gets the whole chain of exception causes to decide what to do.
55
55
  """
56
56
  width = 75
57
+ spacing = 4
57
58
  inset = 5
58
59
  if header is not None:
59
- header = f'{"=" * inset} {header} {"=" * (width - inset - len(header))}'
60
+ header = f'{"=" * inset} {header} {"=" * (width - spacing - inset - len(header))}'
60
61
  else:
61
62
  header = '=' * width
62
63
  logger.error(header)
@@ -86,10 +87,12 @@ def user_exception_block(logger: Logger, exception: AppDaemonException, app_dir:
86
87
  logger.error(f'{indent}{filename} line {line} in {func_name}')
87
88
  case OSError() if str(exc).endswith('address already in use'):
88
89
  logger.error(f'{indent}{exc.__class__.__name__}: {exc}')
89
- case NameError():
90
+ case NameError() | ImportError():
90
91
  logger.error(f'{indent}{exc.__class__.__name__}: {exc}')
91
92
  if tb := traceback.extract_tb(exc.__traceback__):
92
93
  frame = tb[-1]
94
+ file = Path(frame.filename).relative_to(app_dir.parent)
95
+ logger.error(f'{indent} line {frame.lineno} in {file.name}')
93
96
  logger.error(f'{indent} {frame._line.rstrip()}')
94
97
  error_len = frame.end_colno - frame.colno
95
98
  logger.error(f'{indent} {" " * (frame.colno - 1)}{"^" * error_len}')
@@ -112,7 +115,7 @@ def user_exception_block(logger: Logger, exception: AppDaemonException, app_dir:
112
115
  for line in lines:
113
116
  logger.error(f'{indent}{line}')
114
117
 
115
- logger.error('=' * 75)
118
+ logger.error('=' * width)
116
119
 
117
120
 
118
121
  def unexpected_block(logger: Logger, exception: Exception):
@@ -318,7 +321,7 @@ class HTTPHostError(AppDaemonException):
318
321
 
319
322
  def __str__(self):
320
323
  res = "Invalid host specified in URL for HTTP component\n"
321
- res += "As of AppDaemon 4.5 the host name specificed in the URL must resolve to a known host\n"
324
+ res += "As of AppDaemon 4.5 the host name specified in the URL must resolve to a known host\n"
322
325
  res += "You can restore previous behavior by using `0.0.0.0` as the host portion of the URL\n"
323
326
  res += f"For instance: `http://0.0.0.0:{self.port}`\n"
324
327
  return res
@@ -96,9 +96,7 @@ def route_secure(myfunc):
96
96
  return await myfunc(*args)
97
97
 
98
98
  elif "adcreds" in request.cookies:
99
- match = await utils.run_in_executor(
100
- self, bcrypt.checkpw, str.encode(self.password), str.encode(request.cookies["adcreds"])
101
- )
99
+ match = await utils.run_in_executor(self, bcrypt.checkpw, str.encode(self.password), str.encode(request.cookies["adcreds"]))
102
100
  if match:
103
101
  return await myfunc(*args)
104
102
 
@@ -117,13 +115,14 @@ class HTTP:
117
115
  AD: "AppDaemon"
118
116
  """Reference to the AppDaemon container object
119
117
  """
118
+ name: str = "_http"
120
119
 
121
120
  stopping: bool
122
121
  executor: concurrent.futures.ThreadPoolExecutor
123
122
 
124
123
  def __init__(self, ad: "AppDaemon", dashboard, old_admin, admin, api, http):
125
124
  self.AD = ad
126
- self.logger = self.logging.get_child("_http")
125
+ self.logger = self.logging.get_child(self.name)
127
126
  self.access = self.logging.get_access()
128
127
 
129
128
  self.dashboard = dashboard
@@ -175,12 +174,19 @@ class HTTP:
175
174
 
176
175
  try:
177
176
  url = urlparse(self.url)
178
-
179
177
  self.host = url.hostname
180
- try:
178
+ self.scheme = url.scheme
179
+
180
+ if url.port is None:
181
+ match url.scheme:
182
+ case "http":
183
+ self.port = 80
184
+ case "https":
185
+ self.port = 443
186
+ case _:
187
+ self.port = None # let the validation catch it
188
+ else:
181
189
  self.port = url.port
182
- except IndexError:
183
- self.port = 80
184
190
 
185
191
  if self.host == "":
186
192
  raise ValueError("Invalid host for 'url'")
@@ -372,9 +378,7 @@ class HTTP:
372
378
 
373
379
  self._process_arg("url", http)
374
380
  if not self.url:
375
- self.logger.warning(
376
- "'{arg}' is '{value}'. Please configure appdaemon.yaml".format(arg="url", value=self.url)
377
- )
381
+ self.logger.warning("'{arg}' is '{value}'. Please configure appdaemon.yaml".format(arg="url", value=self.url))
378
382
  exit(0)
379
383
 
380
384
  self._process_arg("transport", http)
@@ -893,7 +897,7 @@ class HTTP:
893
897
  return self.get_response(request, code, "App Not Found")
894
898
 
895
899
  elif code == 500:
896
- return self.get_response(request, code, "An Error occured while processing request")
900
+ return self.get_response(request, code, "An Error occurred while processing request")
897
901
 
898
902
  response = "OK"
899
903
  self.access.info("API Call to %s: status: %s %s", endpoint, code, response)
@@ -71,7 +71,7 @@ class LoadingActions:
71
71
  Uses a dependency graph to sort the internal ``init`` and ``reload`` sets together
72
72
  """
73
73
  items = copy(self.init_set)
74
- items |= find_all_dependents(items, dm.app_deps.rev_graph)
74
+ items |= find_all_dependents(items, dm.app_deps.dep_graph)
75
75
  priorities = {
76
76
  app_name: dm.app_deps.app_config.root[app_name].priority
77
77
  for app_name in items
@@ -384,7 +384,7 @@ class Hass(ADBase, ADAPI):
384
384
  case Iterable():
385
385
  constraints = value if isinstance(value, list) else list(value)
386
386
 
387
- assert isinstance(value, list) and all(isinstance(v, str) for v in value)
387
+ assert isinstance(constraints, list) and all(isinstance(v, str) for v in constraints)
388
388
 
389
389
  for constraint in constraints:
390
390
  parts = re.split(r',\s*', constraint)
@@ -475,7 +475,7 @@ class Hass(ADBase, ADAPI):
475
475
  ``timeout``. If ``timeout`` is shorter than this one, it will trigger before this one does.
476
476
  suppress_log_messages (bool, optional): Only applicable to the Hass plugin. If this is set to ``True``,
477
477
  Appdaemon will suppress logging of warnings for service calls to Home Assistant, specifically timeouts
478
- and non OK statuses. Use this flag and set it to ``True`` to supress these log messages if you are
478
+ and non OK statuses. Use this flag and set it to ``True`` to suppress these log messages if you are
479
479
  performing your own error checking as described `here <APPGUIDE.html#some-notes-on-service-calls>`__
480
480
  service_data (dict, optional): Used as an additional dictionary to pass arguments into the ``service_data``
481
481
  field of the JSON that goes to Home Assistant. This is useful if you have a dictionary that you want to
@@ -1,5 +1,5 @@
1
1
  """
2
- Interface with Home Assistant, send and recieve evets, state etc.
2
+ Interface with Home Assistant, send and receive evets, state etc.
3
3
  """
4
4
 
5
5
  import asyncio
@@ -661,7 +661,7 @@ class HassPlugin(PluginBase):
661
661
  shorter than this one, it will trigger before this one does.
662
662
  suppress_log_messages (bool, optional): If this is set to ``True``, Appdaemon will suppress logging of
663
663
  warnings for service calls to Home Assistant, specifically timeouts and non OK statuses. Use this flag
664
- and set it to ``True`` to supress these log messages if you are performing your own error checking as
664
+ and set it to ``True`` to suppress these log messages if you are performing your own error checking as
665
665
  described `here <APPGUIDE.html#some-notes-on-service-calls>`__
666
666
  service_data (dict, optional): Used as an additional dictionary to pass arguments into the ``service_data``
667
667
  field of the JSON that goes to Home Assistant. This is useful if you have a dictionary that you want to
@@ -120,7 +120,7 @@ class Mqtt(adbase.ADBase, adapi.ADAPI):
120
120
 
121
121
  binary (bool, optional): If wanting the payload to be returned as binary, this should
122
122
  be specified. If not given, AD will return the payload as decoded data. It should
123
- be noted that it is not possible to have different apps receieve both binary and non-binary
123
+ be noted that it is not possible to have different apps receive both binary and non-binary
124
124
  data on the same topic
125
125
 
126
126
  Returns:
@@ -229,23 +229,17 @@ class Mqtt(adbase.ADBase, adapi.ADAPI):
229
229
  result = self.call_service(service, **kwargs)
230
230
  return result
231
231
 
232
- def _run_service_call(self, task: str, topic: Union[str, list], **kwargs: Optional[Any]) -> None:
232
+ def _run_service_call(self, task: str, topic: str | list[str], **kwargs: Optional[Any]) -> None:
233
233
  """Used to process the subscribe/unsubscribe service calls"""
234
234
 
235
235
  # first we validate the topic
236
236
  if not isinstance(topic, (str, list)):
237
237
  raise ValueError(f"The given topic {topic} is not supported. Please only strs and lists are supported")
238
238
 
239
- if isinstance(topic, str):
240
- kwargs["topic"] = topic
241
- service = f"mqtt/{task}"
242
- self.call_service(service, **kwargs)
239
+ kwargs["topic"] = topic
240
+ service = f"mqtt/{task}"
241
+ return self.call_service(service, **kwargs)
243
242
 
244
- else: # its a list
245
- for t in topic:
246
- kwargs["topic"] = t
247
- service = f"mqtt/{task}"
248
- self.call_service(service, **kwargs)
249
243
 
250
244
  def mqtt_subscribe(self, topic: Union[str, list], **kwargs: Optional[Any]) -> None:
251
245
  """Subscribes to a MQTT topic.
@@ -284,7 +278,7 @@ class Mqtt(adbase.ADBase, adapi.ADAPI):
284
278
 
285
279
  """
286
280
 
287
- self._run_service_call("subscribe", topic, **kwargs)
281
+ return self._run_service_call("subscribe", topic, **kwargs)
288
282
 
289
283
  def mqtt_unsubscribe(self, topic: Union[str, list], **kwargs: Optional[Any]) -> None:
290
284
  """Unsubscribes from a MQTT topic.
@@ -323,7 +317,7 @@ class Mqtt(adbase.ADBase, adapi.ADAPI):
323
317
 
324
318
  """
325
319
 
326
- self._run_service_call("unsubscribe", topic, **kwargs)
320
+ return self._run_service_call("unsubscribe", topic, **kwargs)
327
321
 
328
322
  @utils.sync_decorator
329
323
  async def is_client_connected(self, **kwargs: Optional[Any]) -> bool: