pyobs-core 2.0.0.dev1__tar.gz → 2.0.0.dev3__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 (450) hide show
  1. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/PKG-INFO +1 -1
  2. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/application.py +11 -2
  3. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/comm.py +95 -5
  4. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/commlogging.py +5 -2
  5. pyobs_core-2.0.0.dev3/pyobs/comm/local/localcomm.py +157 -0
  6. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/proxy.py +25 -0
  7. pyobs_core-2.0.0.dev3/pyobs/comm/xmpp/rpc.py +303 -0
  8. pyobs_core-2.0.0.dev3/pyobs/comm/xmpp/serializer.py +338 -0
  9. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009_timeout/rpc.py +6 -19
  10. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xmppcomm.py +230 -98
  11. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/log.py +18 -2
  12. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IBinning.py +12 -18
  13. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IConfig.py +6 -24
  14. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ICooling.py +8 -20
  15. pyobs_core-2.0.0.dev3/pyobs/interfaces/IExposure.py +26 -0
  16. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IExposureTime.py +9 -18
  17. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFilters.py +9 -16
  18. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFocuser.py +10 -18
  19. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IGain.py +10 -18
  20. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IImageFormat.py +11 -18
  21. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IImageType.py +9 -9
  22. pyobs_core-2.0.0.dev3/pyobs/interfaces/IMode.py +40 -0
  23. pyobs_core-2.0.0.dev3/pyobs/interfaces/IModule.py +26 -0
  24. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IMotion.py +13 -12
  25. pyobs_core-2.0.0.dev3/pyobs/interfaces/IMultiFiber.py +45 -0
  26. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IOffsetsAltAz.py +8 -11
  27. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IOffsetsRaDec.py +8 -11
  28. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IPointingAltAz.py +8 -9
  29. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IPointingHGS.py +8 -11
  30. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IPointingHelioprojective.py +8 -11
  31. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IPointingRaDec.py +8 -9
  32. pyobs_core-2.0.0.dev3/pyobs/interfaces/IReady.py +21 -0
  33. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IRotation.py +9 -5
  34. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IRunning.py +9 -0
  35. pyobs_core-2.0.0.dev3/pyobs/interfaces/ITemperatures.py +30 -0
  36. pyobs_core-2.0.0.dev3/pyobs/interfaces/IVideo.py +19 -0
  37. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IWindow.py +12 -16
  38. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/__init__.py +0 -1
  39. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/interface.py +0 -2
  40. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/mixins/fitsheader.py +5 -2
  41. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/mixins/motionstatus.py +9 -12
  42. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/mixins/waitformotion.py +6 -3
  43. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/mixins/weatheraware.py +1 -1
  44. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/basecamera.py +26 -38
  45. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/basespectrograph.py +0 -8
  46. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/basevideo.py +5 -21
  47. pyobs_core-2.0.0.dev3/pyobs/modules/camera/dummycamera.py +328 -0
  48. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/dummyspectrograph.py +0 -11
  49. pyobs_core-2.0.0.dev3/pyobs/modules/camera/dummyvideo.py +69 -0
  50. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/pipelinecamera.py +1 -10
  51. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/flatfield/flatfield.py +9 -24
  52. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/module.py +49 -4
  53. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/autoguiding.py +1 -11
  54. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/scienceframeguiding.py +0 -16
  55. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/roof/baseroof.py +8 -11
  56. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/roof/dummyroof.py +3 -0
  57. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/telescope/basetelescope.py +16 -27
  58. pyobs_core-2.0.0.dev3/pyobs/modules/telescope/dummytelescope.py +302 -0
  59. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/dummymode.py +12 -35
  60. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/selector.py +2 -1
  61. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/utils/callmodule.py +39 -17
  62. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/parallel.py +15 -3
  63. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyproject.toml +7 -1
  64. pyobs_core-2.0.0.dev1/pyobs/comm/dbus/__init__.py +0 -3
  65. pyobs_core-2.0.0.dev1/pyobs/comm/dbus/dbuscomm.py +0 -591
  66. pyobs_core-2.0.0.dev1/pyobs/comm/dbus/patch.py +0 -112
  67. pyobs_core-2.0.0.dev1/pyobs/comm/local/localcomm.py +0 -98
  68. pyobs_core-2.0.0.dev1/pyobs/comm/xmpp/rpc.py +0 -274
  69. pyobs_core-2.0.0.dev1/pyobs/interfaces/IExposure.py +0 -33
  70. pyobs_core-2.0.0.dev1/pyobs/interfaces/ILatLon.py +0 -41
  71. pyobs_core-2.0.0.dev1/pyobs/interfaces/IMode.py +0 -62
  72. pyobs_core-2.0.0.dev1/pyobs/interfaces/IModule.py +0 -40
  73. pyobs_core-2.0.0.dev1/pyobs/interfaces/IMultiFiber.py +0 -73
  74. pyobs_core-2.0.0.dev1/pyobs/interfaces/IReady.py +0 -22
  75. pyobs_core-2.0.0.dev1/pyobs/interfaces/ITemperatures.py +0 -25
  76. pyobs_core-2.0.0.dev1/pyobs/interfaces/IVideo.py +0 -22
  77. pyobs_core-2.0.0.dev1/pyobs/modules/camera/dummycamera.py +0 -313
  78. pyobs_core-2.0.0.dev1/pyobs/modules/telescope/dummytelescope.py +0 -346
  79. pyobs_core-2.0.0.dev1/pyobs/utils/simulation/__init__.py +0 -11
  80. pyobs_core-2.0.0.dev1/pyobs/utils/simulation/camera.py +0 -284
  81. pyobs_core-2.0.0.dev1/pyobs/utils/simulation/telescope.py +0 -200
  82. pyobs_core-2.0.0.dev1/pyobs/utils/simulation/world.py +0 -107
  83. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/.gitignore +0 -0
  84. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/LICENSE +0 -0
  85. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/__init__.py +0 -0
  86. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/background_task.py +0 -0
  87. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/cli/__init__.py +0 -0
  88. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/cli/_cli.py +0 -0
  89. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/cli/pyobs.py +0 -0
  90. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/cli/pyobsd.py +0 -0
  91. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/cli/pyobsw.py +0 -0
  92. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/__init__.py +0 -0
  93. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/dummy/__init__.py +0 -0
  94. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/dummy/dummycomm.py +0 -0
  95. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/local/__init__.py +0 -0
  96. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/local/localnetwork.py +0 -0
  97. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/__init__.py +0 -0
  98. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009/__init__.py +0 -0
  99. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009/binding.py +0 -0
  100. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009/rpc.py +0 -0
  101. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009_timeout/__init__.py +0 -0
  102. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009_timeout/stanza/RPC.py +0 -0
  103. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xep_0009_timeout/stanza/__init__.py +0 -0
  104. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/comm/xmpp/xmppclient.py +0 -0
  105. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/__init__.py +0 -0
  106. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/badweather.py +0 -0
  107. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/event.py +0 -0
  108. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/exposurestatuschanged.py +0 -0
  109. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/filterchanged.py +0 -0
  110. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/focusfound.py +0 -0
  111. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/goodweather.py +0 -0
  112. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/modechanged.py +0 -0
  113. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/moduleclosed.py +0 -0
  114. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/moduleopened.py +0 -0
  115. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/motionstatuschanged.py +0 -0
  116. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/move.py +0 -0
  117. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/newimage.py +0 -0
  118. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/newspectrum.py +0 -0
  119. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/offsets.py +0 -0
  120. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/roofclosing.py +0 -0
  121. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/roofopened.py +0 -0
  122. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/taskfailed.py +0 -0
  123. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/taskfinished.py +0 -0
  124. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/taskstarted.py +0 -0
  125. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/events/testevent.py +0 -0
  126. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/__init__.py +0 -0
  127. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/image.py +0 -0
  128. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/__init__.py +0 -0
  129. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/altazoffsets.py +0 -0
  130. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/exptime.py +0 -0
  131. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/genericoffset.py +0 -0
  132. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/onskydistance.py +0 -0
  133. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/pixeloffsets.py +0 -0
  134. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/radecoffsets.py +0 -0
  135. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/meta/skyoffsets.py +0 -0
  136. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processor.py +0 -0
  137. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/__init__.py +0 -0
  138. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/_daobackgroundremover.py +0 -0
  139. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/annotation/__init__.py +0 -0
  140. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/annotation/_pillow.py +0 -0
  141. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/annotation/circle.py +0 -0
  142. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/annotation/crosshair.py +0 -0
  143. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/annotation/text.py +0 -0
  144. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/__init__.py +0 -0
  145. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/_dotnet_request.py +0 -0
  146. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/_dotnet_request_builder.py +0 -0
  147. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/_dotnet_request_logger.py +0 -0
  148. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/_dotnet_response_saver.py +0 -0
  149. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/astrometry.py +0 -0
  150. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/astrometry/dotnet.py +0 -0
  151. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/calibration/__init__.py +0 -0
  152. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/calibration/_calibration_cache.py +0 -0
  153. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/calibration/_ccddata_calibrator.py +0 -0
  154. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/calibration/calibration.py +0 -0
  155. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/__init__.py +0 -0
  156. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/_pysep_stats_calculator.py +0 -0
  157. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/_source_catalog.py +0 -0
  158. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/daophot.py +0 -0
  159. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/pysep.py +0 -0
  160. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/simpledisk.py +0 -0
  161. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/detection/sourcedetection.py +0 -0
  162. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/exptime/__init__.py +0 -0
  163. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/exptime/exptime.py +0 -0
  164. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/exptime/star.py +0 -0
  165. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/__init__.py +0 -0
  166. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/addfitsheaders.py +0 -0
  167. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/download.py +0 -0
  168. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/flip.py +0 -0
  169. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/grayscale.py +0 -0
  170. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/httpserver.py +0 -0
  171. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/normalize.py +0 -0
  172. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/save.py +0 -0
  173. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/saveimage.py +0 -0
  174. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/smooth.py +0 -0
  175. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/image/softbin.py +0 -0
  176. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/__init__.py +0 -0
  177. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/addmask.py +0 -0
  178. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/catalogcircularmask.py +0 -0
  179. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/circularmask.py +0 -0
  180. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/createfilename.py +0 -0
  181. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/imagesourcefilter.py +0 -0
  182. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/misc/removebackground.py +0 -0
  183. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/modules/__init__.py +0 -0
  184. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/modules/getfitsheaders.py +0 -0
  185. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/__init__.py +0 -0
  186. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/astrometry.py +0 -0
  187. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/brighteststar.py +0 -0
  188. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/brighteststar_guiding.py +0 -0
  189. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/dummyoffsets.py +0 -0
  190. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/dummyskyoffsets.py +0 -0
  191. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/fitsheader.py +0 -0
  192. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/offsets.py +0 -0
  193. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/pixeloffsets.py +0 -0
  194. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/projected.py +0 -0
  195. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/offsets/spilled_light.py +0 -0
  196. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/__init__.py +0 -0
  197. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/_photometry_calculator.py +0 -0
  198. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/_photutil_aperture_photometry.py +0 -0
  199. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/_sep_aperture_photometry.py +0 -0
  200. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/aperture_photometry.py +0 -0
  201. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/photometry.py +0 -0
  202. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/photutil.py +0 -0
  203. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/photometry/pysep.py +0 -0
  204. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/wcs/__init__.py +0 -0
  205. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/images/processors/wcs/solarhelioprojective.py +0 -0
  206. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IAbortable.py +0 -0
  207. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IAcquisition.py +0 -0
  208. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IAutoFocus.py +0 -0
  209. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IAutoGuiding.py +0 -0
  210. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IAutonomous.py +0 -0
  211. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ICalibrate.py +0 -0
  212. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ICamera.py +0 -0
  213. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IData.py +0 -0
  214. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IDome.py +0 -0
  215. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFitsHeaderAfter.py +0 -0
  216. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFitsHeaderBefore.py +0 -0
  217. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFlatField.py +0 -0
  218. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IFocusModel.py +0 -0
  219. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IPointingSeries.py +0 -0
  220. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IRoof.py +0 -0
  221. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IRunnable.py +0 -0
  222. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IScriptRunner.py +0 -0
  223. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ISpectrograph.py +0 -0
  224. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IStartStop.py +0 -0
  225. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ISyncTarget.py +0 -0
  226. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/ITelescope.py +0 -0
  227. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/interfaces/IWeather.py +0 -0
  228. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/mixins/__init__.py +0 -0
  229. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/mixins/camerasettings.py +0 -0
  230. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/mixins/fitsnamespace.py +0 -0
  231. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/mixins/follow.py +0 -0
  232. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/mixins/pipeline.py +0 -0
  233. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/__init__.py +0 -0
  234. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/__init__.py +0 -0
  235. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/camera/adaptive.py +0 -0
  236. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/flatfield/__init__.py +0 -0
  237. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/flatfield/pointing.py +0 -0
  238. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/flatfield/scheduler.py +0 -0
  239. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/focus/__init__.py +0 -0
  240. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/focus/focusmodel.py +0 -0
  241. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/focus/focusseries.py +0 -0
  242. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/image/__init__.py +0 -0
  243. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/image/imagewatcher.py +0 -0
  244. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/image/imagewriter.py +0 -0
  245. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/image/pipeline.py +0 -0
  246. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/image/seeing.py +0 -0
  247. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/__init__.py +0 -0
  248. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/_base.py +0 -0
  249. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/_baseguiding.py +0 -0
  250. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/acquisition.py +0 -0
  251. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/dummyacquisition.py +0 -0
  252. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/dummyguiding.py +0 -0
  253. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/guidingstatistics/__init__.py +0 -0
  254. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/guidingstatistics/guidingstatistics.py +0 -0
  255. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/guidingstatistics/pixeloffset.py +0 -0
  256. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/guidingstatistics/skyoffsets.py +0 -0
  257. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/pointing/guidingstatistics/uptime.py +0 -0
  258. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/robotic/__init__.py +0 -0
  259. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/robotic/mastermind.py +0 -0
  260. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/robotic/pointing.py +0 -0
  261. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/robotic/scheduler.py +0 -0
  262. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/robotic/scriptrunner.py +0 -0
  263. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/roof/__init__.py +0 -0
  264. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/roof/basedome.py +0 -0
  265. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/telescope/__init__.py +0 -0
  266. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/test/__init__.py +0 -0
  267. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/test/standalone.py +0 -0
  268. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/__init__.py +0 -0
  269. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/autonomouswarning.py +0 -0
  270. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/fluentlogger.py +0 -0
  271. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/httpfilecache.py +0 -0
  272. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/kiosk.py +0 -0
  273. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/matrix.py +0 -0
  274. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/stellarium.py +0 -0
  275. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/telegram.py +0 -0
  276. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/utils/trigger.py +0 -0
  277. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/weather/__init__.py +0 -0
  278. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/weather/weather.py +0 -0
  279. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/weather/weather_api.py +0 -0
  280. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/modules/weather/weather_state.py +0 -0
  281. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/object.py +0 -0
  282. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/py.typed +0 -0
  283. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/__init__.py +0 -0
  284. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/observation.py +0 -0
  285. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/__init__.py +0 -0
  286. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/astroplanscheduler.py +0 -0
  287. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/__init__.py +0 -0
  288. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/airmassconstraint.py +0 -0
  289. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/constraint.py +0 -0
  290. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/moonilluminationconstraint.py +0 -0
  291. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/moonseparationconstraint.py +0 -0
  292. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/solarelevationconstraint.py +0 -0
  293. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/constraints/timeconstraint.py +0 -0
  294. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/dataprovider.py +0 -0
  295. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/__init__.py +0 -0
  296. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/aftertime.py +0 -0
  297. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/beforetime.py +0 -0
  298. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/constant.py +0 -0
  299. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/follow.py +0 -0
  300. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/interval.py +0 -0
  301. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/merit.py +0 -0
  302. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/pernight.py +0 -0
  303. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/random.py +0 -0
  304. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/timewindow.py +0 -0
  305. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/merits/transit.py +0 -0
  306. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/observationarchiveevolution.py +0 -0
  307. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/ondemandscheduler.py +0 -0
  308. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/__init__.py +0 -0
  309. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/dynamictarget.py +0 -0
  310. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/helioprojectiveradialarget.py +0 -0
  311. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/picker/__init__.py +0 -0
  312. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/picker/csvpicker.py +0 -0
  313. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/picker/picker.py +0 -0
  314. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/siderealtarget.py +0 -0
  315. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/targets/target.py +0 -0
  316. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scheduler/taskscheduler.py +0 -0
  317. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/__init__.py +0 -0
  318. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/calibration/__init__.py +0 -0
  319. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/calibration/darkbias.py +0 -0
  320. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/calibration/pointing.py +0 -0
  321. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/calibration/skyflats.py +0 -0
  322. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/__init__.py +0 -0
  323. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/cases.py +0 -0
  324. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/conditional.py +0 -0
  325. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/parallel.py +0 -0
  326. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/control/sequential.py +0 -0
  327. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/imaging/__init__.py +0 -0
  328. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/imaging/autofocus.py +0 -0
  329. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/imaging/imaging.py +0 -0
  330. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/imaging/transitimaging.py +0 -0
  331. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/script.py +0 -0
  332. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/utils/__init__.py +0 -0
  333. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/utils/debugtrigger.py +0 -0
  334. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/scripts/utils/log.py +0 -0
  335. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/__init__.py +0 -0
  336. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/backend/__init__.py +0 -0
  337. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/backend/observationarchive.py +0 -0
  338. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/backend/taskarchive.py +0 -0
  339. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/filesystem/__init__.py +0 -0
  340. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/filesystem/observationarchive.py +0 -0
  341. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/filesystem/taskarchive.py +0 -0
  342. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/__init__.py +0 -0
  343. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/_portal.py +0 -0
  344. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/_schedulereader.py +0 -0
  345. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/_schedulewriter.py +0 -0
  346. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/configdb.py +0 -0
  347. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/mockobservationarchive.py +0 -0
  348. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/observationarchive.py +0 -0
  349. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/scripts/__init__.py +0 -0
  350. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/scripts/autofocus.py +0 -0
  351. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/scripts/default.py +0 -0
  352. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/scripts/script.py +0 -0
  353. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/task.py +0 -0
  354. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/taskarchive.py +0 -0
  355. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/lco/taskrunner.py +0 -0
  356. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/memory/__init__.py +0 -0
  357. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/memory/observationarchive.py +0 -0
  358. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/memory/taskarchive.py +0 -0
  359. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/observationarchive.py +0 -0
  360. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/storage/taskarchive.py +0 -0
  361. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/task.py +0 -0
  362. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/taskrunner.py +0 -0
  363. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/__init__.py +0 -0
  364. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/archive/__init__.py +0 -0
  365. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/archive/archive.py +0 -0
  366. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/archive/local_archive.py +0 -0
  367. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/archive/pyobs_archive.py +0 -0
  368. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/exptime/__init__.py +0 -0
  369. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/exptime/exptime.py +0 -0
  370. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/exptime/stellarexptime.py +0 -0
  371. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/__init__.py +0 -0
  372. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/exptimeeval.py +0 -0
  373. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/flatfielder.py +0 -0
  374. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/pointing/__init__.py +0 -0
  375. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/pointing/base.py +0 -0
  376. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/pointing/static.py +0 -0
  377. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/priorities/__init__.py +0 -0
  378. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/priorities/archive.py +0 -0
  379. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/priorities/base.py +0 -0
  380. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/priorities/const.py +0 -0
  381. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/robotic/utils/skyflats/scheduler.py +0 -0
  382. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/__init__.py +0 -0
  383. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/average.py +0 -0
  384. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/cache.py +0 -0
  385. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/config.py +0 -0
  386. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/coordinates.py +0 -0
  387. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/curvefit.py +0 -0
  388. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/enums.py +0 -0
  389. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/exceptions.py +0 -0
  390. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/fits.py +0 -0
  391. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/focusseries/__init__.py +0 -0
  392. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/focusseries/base.py +0 -0
  393. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/focusseries/photometry.py +0 -0
  394. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/focusseries/projection.py +0 -0
  395. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/grids/__init__.py +0 -0
  396. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/grids/filters.py +0 -0
  397. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/grids/grid.py +0 -0
  398. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/grids/gridnode.py +0 -0
  399. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/grids/pipeline.py +0 -0
  400. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/__init__.py +0 -0
  401. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/__init__.py +0 -0
  402. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/binningwidget.py +0 -0
  403. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/datadisplaywidget.py +0 -0
  404. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/exposewidget.py +0 -0
  405. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/exposuretimewidget.py +0 -0
  406. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/imageformatwidget.py +0 -0
  407. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/listpickerdialog.py +0 -0
  408. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/gui/camera/windowingwidget.py +0 -0
  409. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/http.py +0 -0
  410. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/influxdb.py +0 -0
  411. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/logger.py +0 -0
  412. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/logging/__init__.py +0 -0
  413. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/logging/context.py +0 -0
  414. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/logging/resolvableerror.py +0 -0
  415. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/modulegui.py +0 -0
  416. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/offsets/__init__.py +0 -0
  417. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/offsets/applyaltazoffsets.py +0 -0
  418. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/offsets/applyoffsets.py +0 -0
  419. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/offsets/applyradecoffsets.py +0 -0
  420. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/pipeline/__init__.py +0 -0
  421. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/pipeline/night.py +0 -0
  422. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/pipeline/pipeline.py +0 -0
  423. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/__init__.py +0 -0
  424. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/csv.py +0 -0
  425. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/http.py +0 -0
  426. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/log.py +0 -0
  427. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/multi.py +0 -0
  428. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/publisher/publisher.py +0 -0
  429. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/serialization.py +0 -0
  430. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/shellcommand.py +0 -0
  431. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/threads/__init__.py +0 -0
  432. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/threads/lockwithabort.py +0 -0
  433. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/time.py +0 -0
  434. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/utils/types.py +0 -0
  435. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/version.py +0 -0
  436. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/__init__.py +0 -0
  437. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/archivefile.py +0 -0
  438. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/bufferedfile.py +0 -0
  439. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/file.py +0 -0
  440. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/filelists/__init__.py +0 -0
  441. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/filelists/filelist.py +0 -0
  442. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/filelists/testing.py +0 -0
  443. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/httpfile.py +0 -0
  444. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/localfile.py +0 -0
  445. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/memfile.py +0 -0
  446. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/sftpfile.py +0 -0
  447. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/smbfile.py +0 -0
  448. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/sshfile.py +0 -0
  449. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/tempfile.py +0 -0
  450. {pyobs_core-2.0.0.dev1 → pyobs_core-2.0.0.dev3}/pyobs/vfs/vfs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyobs-core
3
- Version: 2.0.0.dev1
3
+ Version: 2.0.0.dev3
4
4
  Summary: robotic telescope software
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -140,8 +140,17 @@ class Application:
140
140
  class_name = cfg["class"]
141
141
  klass = get_class_from_string(class_name)
142
142
 
143
- # create event loop
144
- self._loop = klass.new_event_loop()
143
+ # create event loop — if top-level class doesn't override new_event_loop,
144
+ # check child modules for one that does (e.g. pyobs_gui.GUI in a MultiModule)
145
+ loop_class = klass
146
+ if klass.new_event_loop is Module.new_event_loop:
147
+ for mod_cfg in cfg.get("modules", {}).values():
148
+ if isinstance(mod_cfg, dict) and "class" in mod_cfg:
149
+ child_klass = get_class_from_string(mod_cfg["class"])
150
+ if child_klass.new_event_loop is not Module.new_event_loop:
151
+ loop_class = child_klass
152
+ break
153
+ self._loop = loop_class.new_event_loop()
145
154
  asyncio.set_event_loop(self._loop)
146
155
 
147
156
  # create module and open it
@@ -4,12 +4,14 @@ import asyncio
4
4
  import functools
5
5
  import inspect
6
6
  import logging
7
+ import sys
7
8
  from collections.abc import Callable, Coroutine
8
9
  from typing import TYPE_CHECKING, Any, overload
9
10
 
10
11
  import pyobs.interfaces
11
12
  from pyobs.events import Event, LogEvent, ModuleClosedEvent
12
13
  from pyobs.interfaces import Interface
14
+ from pyobs.utils.enums import ModuleState
13
15
 
14
16
  from .commlogging import CommLoggingHandler
15
17
  from .proxy import Proxy, ProxyType, _ProxyContext
@@ -18,6 +20,7 @@ if TYPE_CHECKING:
18
20
  from pyobs.modules import Module
19
21
 
20
22
  StateCallback = Callable[[Any], None]
23
+ PresenceCallback = Callable[["ModuleState", str], None]
21
24
 
22
25
  log = logging.getLogger(__name__)
23
26
 
@@ -64,9 +67,14 @@ class Comm:
64
67
  """Open module."""
65
68
 
66
69
  # add handler to global logger
67
- handler = CommLoggingHandler(self)
68
- handler.setLevel(logging.INFO)
69
- logging.getLogger().addHandler(handler)
70
+ root_logger = logging.getLogger()
71
+ if not any(isinstance(h, CommLoggingHandler) for h in root_logger.handlers):
72
+ from pyobs.utils.logging.context import ModuleNameFilter
73
+
74
+ handler = CommLoggingHandler(self)
75
+ handler.setLevel(logging.INFO)
76
+ handler.addFilter(ModuleNameFilter())
77
+ root_logger.addHandler(handler)
70
78
 
71
79
  # start logging thread
72
80
  self._logging_task = asyncio.create_task(self._logging())
@@ -129,7 +137,7 @@ class Comm:
129
137
 
130
138
  # subscribe to state
131
139
  for interface in interfaces:
132
- if getattr(interface, "state", None) is not None:
140
+ if getattr(interface, "State", None) is not None:
133
141
  await self.subscribe_state(client, interface, functools.partial(proxy.update_state, interface))
134
142
 
135
143
  self._proxies[client] = proxy
@@ -430,18 +438,86 @@ class Comm:
430
438
  ) -> None:
431
439
  pass
432
440
 
433
- async def set_state(self, interface: type[Interface], state: Any) -> None:
441
+ @staticmethod
442
+ def _interface_from_state(state_cls: type) -> type:
443
+ outer_name = state_cls.__qualname__.rsplit(".", 1)[0]
444
+ return getattr(sys.modules[state_cls.__module__], outer_name)
445
+
446
+ async def set_state(self, state: Any) -> None:
434
447
  """Publish state for this module.
435
448
 
436
449
  Args:
437
450
  interface: Interface type for the state.
438
451
  state: State object to publish.
439
452
  """
453
+ interface = Comm._interface_from_state(type(state))
440
454
  await self._set_state(interface, state)
441
455
 
442
456
  async def _set_state(self, interface: type[Interface], state: Any) -> None:
443
457
  pass
444
458
 
459
+ @staticmethod
460
+ def _interface_from_capabilities(caps_cls: type) -> type:
461
+ outer_name = caps_cls.__qualname__.rsplit(".", 1)[0]
462
+ return getattr(sys.modules[caps_cls.__module__], outer_name)
463
+
464
+ async def set_capabilities(self, capabilities: Any) -> None:
465
+ """Publish capabilities for this module.
466
+
467
+ Called by Module.open() for each interface that defines a Capabilities
468
+ dataclass. Not intended to be called directly by module authors after
469
+ that point — capabilities are fixed for the module lifetime.
470
+
471
+ Args:
472
+ capabilities: Capabilities dataclass instance.
473
+ """
474
+ interface = Comm._interface_from_capabilities(type(capabilities))
475
+ await self._set_capabilities(interface, capabilities)
476
+
477
+ async def _set_capabilities(self, interface: type[Interface], capabilities: Any) -> None:
478
+ pass
479
+
480
+ async def get_capabilities(self, module: str, interface: type[Interface]) -> Any | None:
481
+ """Fetch and deserialize capabilities for a remote module's interface.
482
+
483
+ Args:
484
+ module: Module name (e.g. "camera").
485
+ interface: Interface class whose Capabilities dataclass to fetch.
486
+
487
+ Returns:
488
+ Deserialized Capabilities dataclass instance, or None if not published.
489
+ """
490
+ return await self._get_capabilities(module, interface)
491
+
492
+ async def _get_capabilities(self, module: str, interface: type[Interface]) -> Any | None:
493
+ return None
494
+
495
+ async def set_presence(self, state: ModuleState, error_string: str = "") -> None:
496
+ """Publish presence for this module (module lifecycle state).
497
+
498
+ Called automatically by Module.set_state — not intended to be called
499
+ directly by module authors.
500
+
501
+ Args:
502
+ state: Current module lifecycle state.
503
+ error_string: Error message, used when state is ERROR.
504
+ """
505
+ await self._set_presence(state, error_string)
506
+
507
+ async def _set_presence(self, state: ModuleState, error_string: str = "") -> None:
508
+ pass
509
+
510
+ def get_client_state(self, module: str) -> tuple[ModuleState, str] | None:
511
+ """Return the last known presence state of a connected module.
512
+
513
+ Returns (ModuleState, error_string) or None if the module is not connected.
514
+ This replaces the old get_state()/get_error_string() RPC pattern.
515
+ """
516
+ return self._get_client_state(module)
517
+
518
+ def _get_client_state(self, module: str) -> tuple[ModuleState, str] | None:
519
+ return None
520
+
445
521
  async def subscribe_state(
446
522
  self,
447
523
  module: str,
@@ -491,6 +567,20 @@ class Comm:
491
567
  ) -> None:
492
568
  pass
493
569
 
570
+ async def subscribe_presence(self, module: str, callback: PresenceCallback) -> None:
571
+ """Subscribe to presence updates for a given module.
572
+
573
+ Delivers the current value immediately, then on every change.
574
+
575
+ Args:
576
+ module: Name of remote module.
577
+ callback: Called with (ModuleState, error_string) on each update.
578
+ """
579
+ await self._subscribe_presence(module, callback)
580
+
581
+ async def _subscribe_presence(self, module: str, callback: PresenceCallback) -> None:
582
+ pass
583
+
494
584
  def _send_event_to_module(self, event: Event, from_client: str) -> None:
495
585
  """Send an event to all connected modules.
496
586
 
@@ -34,9 +34,12 @@ class CommLoggingHandler(logging.Handler):
34
34
  # format message
35
35
  msg = self._formatter.format(rec) # noqa: UP031
36
36
 
37
- # create and send event
37
+ # create and send event — use pyobs_module if set (MultiModule context)
38
38
  time = datetime.fromtimestamp(rec.created, tz=UTC).strftime("%Y-%m-%dT%H:%M:%S.%f")
39
- entry = LogEvent(time, rec.levelname, os.path.basename(rec.pathname), rec.funcName, rec.lineno, msg)
39
+ sender = getattr(rec, "pyobs_module", "") or ""
40
+ entry = LogEvent(
41
+ time, rec.levelname, os.path.basename(rec.pathname), rec.funcName, rec.lineno, msg, sender=sender
42
+ )
40
43
  self._comm.log_message(entry)
41
44
 
42
45
 
@@ -0,0 +1,157 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Coroutine
4
+ from typing import Any
5
+
6
+ from pyobs.comm import Comm
7
+ from pyobs.comm.comm import PresenceCallback
8
+ from pyobs.events import Event
9
+ from pyobs.interfaces import Interface
10
+ from pyobs.utils.enums import ModuleState
11
+ from pyobs.utils.types import cast_response_to_real
12
+
13
+ from .localnetwork import LocalNetwork
14
+
15
+
16
+ class LocalComm(Comm):
17
+ def __init__(self, name: str, *args: Any, **kwargs: Any):
18
+ Comm.__init__(self, *args, **kwargs)
19
+
20
+ self._name = name
21
+ self._network = LocalNetwork()
22
+ self._network.connect_client(self)
23
+
24
+ # in-memory state and capabilities storage
25
+ self._states: dict[str, Any] = {} # node -> state object
26
+ self._state_handlers: dict[str, list[tuple[type[Interface], Callable[[Any], None]]]] = {}
27
+ self._capabilities: dict[type[Interface], Any] = {} # interface -> Capabilities object
28
+ self._presence: tuple[ModuleState, str] = (ModuleState.READY, "")
29
+ self._presence_callbacks: dict[str, list[PresenceCallback]] = {}
30
+
31
+ @property
32
+ def name(self) -> str:
33
+ """Name of this client."""
34
+ return self._name
35
+
36
+ @property
37
+ def clients(self) -> list[str]:
38
+ """Returns list of currently connected clients."""
39
+ return self._network.get_client_names()
40
+
41
+ async def get_interfaces(self, client: str) -> list[type[Interface]]:
42
+ """Returns list of interfaces for given client."""
43
+ remote_client = self._network.get_client(client)
44
+ return [] if not remote_client.has_module else remote_client.module.interfaces
45
+
46
+ async def _supports_interface(self, client: str, interface: type[Interface]) -> bool:
47
+ """Checks whether the given client supports the given interface."""
48
+ interfaces = await self.get_interfaces(client)
49
+ return interface in interfaces
50
+
51
+ async def execute(self, client: str, method: str, annotation: dict[str, Any], *args: Any) -> Any:
52
+ """Execute a given method on a remote client."""
53
+ remote_client = self._network.get_client(client)
54
+ if remote_client.module is None:
55
+ raise ValueError
56
+ simple_results = await remote_client.module.execute(method, *args, sender=self.name)
57
+ real_results = cast_response_to_real(
58
+ simple_results, annotation["return"], self.cast_to_real_pre, self.cast_to_real_post
59
+ )
60
+ return real_results
61
+
62
+ async def send_event(self, event: Event) -> None:
63
+ """Send an event to other clients."""
64
+ from pyobs.events import LogEvent as _LogEvent
65
+
66
+ sender = self.name
67
+ if isinstance(event, _LogEvent) and event.sender:
68
+ sender = event.sender
69
+ remote_clients = self._network.get_clients()
70
+ for client in remote_clients:
71
+ client._send_event_to_module(event, sender)
72
+
73
+ async def _register_events(
74
+ self, events: list[type[Event]], handler: Callable[[Event, str], Coroutine[Any, Any, bool]] | None = None
75
+ ) -> None:
76
+ pass
77
+
78
+ # -------------------------------------------------------------------------
79
+ # State
80
+ # -------------------------------------------------------------------------
81
+
82
+ async def _set_state(self, interface: type[Interface], state: Any) -> None:
83
+ """Publish state locally and dispatch to subscribers."""
84
+ node = f"pyobs:state:{self._name}:{interface.__name__}:{interface.version}"
85
+ self._states[node] = state
86
+
87
+ # dispatch to all subscribers for this node
88
+ if node in self._state_handlers:
89
+ for _, callback in self._state_handlers[node]:
90
+ callback(state)
91
+
92
+ async def _subscribe_state(self, module: str, interface: type[Interface], callback: Callable[[Any], None]) -> None:
93
+ """Subscribe to state updates from a remote module."""
94
+ node = f"pyobs:state:{module}:{interface.__name__}:{interface.version}"
95
+
96
+ if node not in self._state_handlers:
97
+ self._state_handlers[node] = []
98
+ self._state_handlers[node].append((interface, callback))
99
+
100
+ # also register on the remote comm so it can dispatch to us
101
+ try:
102
+ remote = self._network.get_client(module)
103
+ if node not in remote._state_handlers:
104
+ remote._state_handlers[node] = []
105
+ remote._state_handlers[node].append((interface, callback))
106
+
107
+ # deliver current value immediately if available
108
+ if node in remote._states:
109
+ callback(remote._states[node])
110
+ except KeyError:
111
+ pass # remote not connected yet
112
+
113
+ # -------------------------------------------------------------------------
114
+ # Capabilities
115
+ # -------------------------------------------------------------------------
116
+
117
+ async def _set_capabilities(self, interface: type[Interface], capabilities: Any) -> None:
118
+ """Store capabilities locally."""
119
+ self._capabilities[interface] = capabilities
120
+
121
+ async def _get_capabilities(self, module: str, interface: type[Interface]) -> Any | None:
122
+ """Fetch capabilities from a remote module."""
123
+ if not hasattr(interface, "Capabilities"):
124
+ return None
125
+ try:
126
+ remote = self._network.get_client(module)
127
+ return remote._capabilities.get(interface)
128
+ except KeyError:
129
+ return None
130
+
131
+ # -------------------------------------------------------------------------
132
+ # Presence
133
+ # -------------------------------------------------------------------------
134
+
135
+ async def _set_presence(self, state: ModuleState, error_string: str = "") -> None:
136
+ """Store presence state and dispatch to all subscribers."""
137
+ self._presence = (state, error_string)
138
+ for client in self._network.get_clients():
139
+ for cb in client._presence_callbacks.get(self._name, []):
140
+ cb(state, error_string)
141
+
142
+ def _get_client_state(self, module: str) -> tuple[ModuleState, str] | None:
143
+ """Return presence state of a connected module."""
144
+ try:
145
+ remote = self._network.get_client(module)
146
+ return remote._presence
147
+ except KeyError:
148
+ return None
149
+
150
+ async def _subscribe_presence(self, module: str, callback: PresenceCallback) -> None:
151
+ """Register a presence callback and deliver the current state immediately."""
152
+ self._presence_callbacks.setdefault(module, []).append(callback)
153
+ try:
154
+ remote = self._network.get_client(module)
155
+ callback(*remote._presence)
156
+ except KeyError:
157
+ pass
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import inspect
4
5
  import types
5
6
  from collections.abc import Coroutine
@@ -169,6 +170,30 @@ class Proxy:
169
170
  """Latest known state for the given interface, or None if nothing has arrived yet."""
170
171
  return self._state.get(interface)
171
172
 
173
+ async def wait_for_state(
174
+ self,
175
+ interface: type[Interface],
176
+ timeout: float = 10.0,
177
+ ) -> Any:
178
+ """Return state immediately if available, otherwise wait for the first update."""
179
+ if self._state.get(interface) is not None:
180
+ return self._state[interface]
181
+
182
+ event = asyncio.Event()
183
+
184
+ def _notify(state: Any) -> None:
185
+ self._state[interface] = state
186
+ event.set()
187
+
188
+ # patch in a one-shot notifier alongside the existing callback
189
+ await self._comm.subscribe_state(self._client, interface, _notify)
190
+ try:
191
+ await asyncio.wait_for(event.wait(), timeout=timeout)
192
+ finally:
193
+ await self._comm.unsubscribe_state(self._client, interface, _notify)
194
+
195
+ return self._state.get(interface)
196
+
172
197
 
173
198
  class _ProxyContext(Generic[ProxyType]):
174
199
  """Returned by Comm.proxy() / Object.proxy() / Comm.safe_proxy(). Must be used as: