pyobs-core 2.0.0.dev90__tar.gz → 2.0.0.dev92__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.
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/PKG-INFO +1 -1
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/application.py +10 -1
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/mixins/weatheraware.py +1 -1
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/camera/basecamera.py +36 -3
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/camera/basevideo.py +199 -2
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/image/imagewatcher.py +9 -1
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/flatfielder.py +1 -1
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/httpfile.py +8 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyproject.toml +1 -1
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/.gitignore +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/LICENSE +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/background_task.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/cli/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/cli/_cli.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/cli/pyobs.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/cli/pyobsd.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/cli/pyobsw.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/comm.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/commlogging.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/dummy/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/dummy/dummycomm.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/local/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/local/localcomm.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/local/localnetwork.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/proxy.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/rpc.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/serializer.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xep_0009/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xep_0009/binding.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xep_0009/rpc.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xep_0009_timeout/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xep_0009_timeout/rpc.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xep_0009_timeout/stanza/RPC.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xep_0009_timeout/stanza/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xmppclient.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xmppcomm.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/badweather.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/event.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/exposurestatuschanged.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/filterchanged.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/focusfound.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/goodweather.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/log.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/modechanged.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/moduleclosed.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/moduleopened.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/motionstatuschanged.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/move.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/newimage.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/newspectrum.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/offsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/roofclosing.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/roofopened.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/taskfailed.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/taskfinished.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/taskstarted.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/events/testevent.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/image.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/meta/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/meta/altazoffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/meta/exptime.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/meta/genericoffset.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/meta/onskydistance.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/meta/pixeloffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/meta/radecoffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/meta/skyoffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processor.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/_daobackgroundremover.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/annotation/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/annotation/_pillow.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/annotation/circle.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/annotation/crosshair.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/annotation/text.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/astrometry/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/astrometry/_dotnet_request.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/astrometry/_dotnet_request_builder.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/astrometry/_dotnet_request_logger.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/astrometry/_dotnet_response_saver.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/astrometry/astrometry.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/astrometry/dotnet.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/calibration/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/calibration/_calibration_cache.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/calibration/_ccddata_calibrator.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/calibration/calibration.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/detection/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/detection/_pysep_stats_calculator.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/detection/_source_catalog.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/detection/daophot.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/detection/pysep.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/detection/simpledisk.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/detection/sourcedetection.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/exptime/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/exptime/exptime.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/exptime/star.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/addfitsheaders.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/download.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/flip.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/grayscale.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/httpserver.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/normalize.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/save.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/saveimage.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/smooth.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/image/softbin.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/misc/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/misc/addmask.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/misc/catalogcircularmask.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/misc/circularmask.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/misc/createfilename.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/misc/imagesourcefilter.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/misc/removebackground.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/modules/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/modules/getfitsheaders.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/astrometry.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/brighteststar.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/brighteststar_guiding.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/dummyoffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/dummyskyoffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/fitsheader.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/offsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/pixeloffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/projected.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/offsets/spilled_light.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/photometry/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/photometry/_photometry_calculator.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/photometry/_photutil_aperture_photometry.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/photometry/_sep_aperture_photometry.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/photometry/aperture_photometry.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/photometry/photometry.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/photometry/photutil.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/photometry/pysep.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/wcs/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/wcs/solarhelioprojective.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IAbortable.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IAcquisition.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IAutoFocus.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IAutoGuiding.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IAutonomous.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IBinning.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/ICalibrate.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/ICamera.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IConfig.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/ICooling.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IData.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IDataSequence.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IDome.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IExposure.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IExposureTime.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IFilters.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IFitsHeaderAfter.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IFitsHeaderBefore.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IFlatField.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IFocusModel.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IFocuser.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IGain.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IImageFormat.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IImageType.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IMode.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IModule.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IMotion.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IMultiFiber.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IOffsetsAltAz.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IOffsetsRaDec.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IPointingAltAz.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IPointingBody.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IPointingHeliocentricPolar.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IPointingHeliographicStonyhurst.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IPointingHelioprojective.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IPointingOrbitalElements.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IPointingRaDec.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IPointingSeries.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IReady.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IRoof.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IRotation.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IRunnable.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IRunning.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IScriptRunner.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/ISpectrograph.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IStartStop.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IStructuredConfig.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/ISyncTarget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/ITelescope.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/ITemperatures.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/ITrackingMode.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/ITrackingRate.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IVideo.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IWeather.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/IWindow.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/interfaces/interface.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/mixins/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/mixins/camerasettings.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/mixins/fitsheader.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/mixins/fitsnamespace.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/mixins/follow.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/mixins/motionstatus.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/mixins/pipeline.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/mixins/waitformotion.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/camera/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/camera/adaptive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/camera/basespectrograph.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/camera/dummycamera.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/camera/dummyspectrograph.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/camera/dummyvideo.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/camera/pipelinecamera.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/flatfield/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/flatfield/flatfield.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/flatfield/pointing.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/flatfield/scheduler.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/focus/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/focus/dummyautofocus.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/focus/focusmodel.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/focus/focusseries.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/image/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/image/imagewriter.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/image/pipeline.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/image/seeing.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/module.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/_base.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/_baseguiding.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/acquisition.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/autoguiding.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/dummyacquisition.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/dummyguiding.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/guidingstatistics/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/guidingstatistics/guidingstatistics.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/guidingstatistics/pixeloffset.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/guidingstatistics/skyoffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/guidingstatistics/uptime.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/pointing/scienceframeguiding.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/robotic/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/robotic/mastermind.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/robotic/pointing.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/robotic/scheduler.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/robotic/scriptrunner.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/roof/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/roof/basedome.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/roof/baseroof.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/roof/dummyroof.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/telescope/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/telescope/_dummytelescopebase.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/telescope/basetelescope.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/telescope/dummyaltaztelescope.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/telescope/dummyradectelescope.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/telescope/dummysolartelescope.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/test/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/test/standalone.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/utils/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/utils/dummymode.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/utils/fluentlogger.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/utils/httpfilecache.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/utils/kiosk.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/utils/matrix.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/utils/stellarium.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/utils/telegram.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/utils/trigger.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/weather/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/weather/mockweather.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/weather/weather.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/weather/weather_api.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/modules/weather/weather_state.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/object.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/py.typed +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/observation.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/_executor.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/astroplanscheduler.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/constraints/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/constraints/airmassconstraint.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/constraints/constraint.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/constraints/moonilluminationconstraint.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/constraints/moonseparationconstraint.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/constraints/solarelevationconstraint.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/constraints/timeconstraint.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/dataprovider.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/aftertime.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/beforetime.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/constant.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/follow.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/interval.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/merit.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/pernight.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/random.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/timewindow.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/merits/transit.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/observationarchiveevolution.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/ondemandscheduler.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/targets/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/targets/dynamictarget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/targets/heliocentricpolartarget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/targets/helioprojectivetarget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/targets/picker/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/targets/picker/csvpicker.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/targets/picker/picker.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/targets/siderealtarget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/targets/target.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scheduler/taskscheduler.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/calibration/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/calibration/darkbias.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/calibration/pointing.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/calibration/skyflats.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/control/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/control/cases.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/control/conditional.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/control/parallel.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/control/selector.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/control/sequential.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/exceptions.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/imaging/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/imaging/autofocus.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/imaging/imaging.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/imaging/transitimaging.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/script.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/utils/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/utils/callmodule.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/utils/debugtrigger.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/scripts/utils/log.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/backend/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/backend/observationarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/backend/taskarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/filesystem/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/filesystem/observationarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/filesystem/taskarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/_portal.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/_schedulereader.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/_schedulewriter.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/configdb.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/mockobservationarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/observationarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/scripts/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/scripts/autofocus.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/scripts/default.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/scripts/script.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/task.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/taskarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/lco/taskrunner.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/memory/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/memory/observationarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/memory/taskarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/observationarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/storage/taskarchive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/task.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/taskrunner.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/archive/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/archive/archive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/archive/local_archive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/archive/pyobs_archive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/exptime/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/exptime/exptime.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/exptime/stellarexptime.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/exptimeeval.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/pointing/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/pointing/base.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/pointing/static.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/priorities/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/priorities/archive.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/priorities/base.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/priorities/const.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/scheduler.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/average.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/cache.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/config.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/config_schema.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/coordinates.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/curvefit.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/enums.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/exceptions.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/fits.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/focusseries/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/focusseries/base.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/focusseries/photometry.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/focusseries/projection.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/grids/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/grids/filters.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/grids/grid.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/grids/gridnode.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/grids/pipeline.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/gui/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/gui/camera/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/gui/camera/binningwidget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/gui/camera/datadisplaywidget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/gui/camera/exposewidget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/gui/camera/exposuretimewidget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/gui/camera/imageformatwidget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/gui/camera/listpickerdialog.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/gui/camera/windowingwidget.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/http.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/influxdb.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/logger.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/logging/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/logging/context.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/logging/resolvableerror.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/modulegui.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/offsets/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/offsets/applyaltazoffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/offsets/applyoffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/offsets/applyradecoffsets.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/parallel.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/pipeline/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/pipeline/pipeline.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/pipeline/progress.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/pipeline/reduction.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/pipeline/reduction_base.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/publisher/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/publisher/csv.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/publisher/http.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/publisher/log.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/publisher/multi.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/publisher/publisher.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/serialization.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/shellcommand.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/threads/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/threads/lockwithabort.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/time.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/units.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/utils/versions.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/version.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/archivefile.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/bufferedfile.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/file.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/filelists/__init__.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/filelists/filelist.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/filelists/testing.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/localfile.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/memfile.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/sftpfile.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/smbfile.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/sshfile.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/tempfile.py +0 -0
- {pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/vfs/vfs.py +0 -0
|
@@ -260,7 +260,16 @@ class Application:
|
|
|
260
260
|
# stem-mismatch check below. Give it the config file's own stem instead.
|
|
261
261
|
cfg = {**cfg, "comm": {"class": f"{DummyComm.__module__}.{DummyComm.__name__}", "name": config_stem}}
|
|
262
262
|
log.info("Creating module from class %s...", klass.__name__)
|
|
263
|
-
|
|
263
|
+
try:
|
|
264
|
+
self._module = get_object(cfg, Module)
|
|
265
|
+
except Exception:
|
|
266
|
+
# The module-creation failure (bad kwargs, broken __init__ chain, ...) is
|
|
267
|
+
# otherwise only visible as an uncaught traceback on stderr -- which is
|
|
268
|
+
# /dev/null for daemonized modules (--pid-file, as started by pyobsd /
|
|
269
|
+
# pyobs-web-admin), so it silently vanishes from all logs. Record it through
|
|
270
|
+
# the configured handlers (file/journald) before propagating.
|
|
271
|
+
log.exception("Could not create module %s from config %s.", klass.__name__, config)
|
|
272
|
+
raise
|
|
264
273
|
|
|
265
274
|
# a config file whose stem doesn't match its own module's name silently splits
|
|
266
275
|
# PYOBS_MODULE log tagging in two: logging that falls through both execute()
|
|
@@ -84,7 +84,7 @@ class WeatherAwareMixin:
|
|
|
84
84
|
if not self.__weatheraware_is_error_state:
|
|
85
85
|
log.error("Telescope is in error mode, cannot park.")
|
|
86
86
|
else:
|
|
87
|
-
log.
|
|
87
|
+
log.info("Weather is bad, shutting down.")
|
|
88
88
|
await msi.park()
|
|
89
89
|
|
|
90
90
|
# save error state
|
|
@@ -524,9 +524,42 @@ class BaseCamera(
|
|
|
524
524
|
is_top = max(top, img_top)
|
|
525
525
|
is_bottom = min(top + height, img_top + img_height)
|
|
526
526
|
|
|
527
|
-
#
|
|
528
|
-
|
|
529
|
-
|
|
527
|
+
# prescan/overscan exists only where the window extends beyond the visible frame
|
|
528
|
+
x_ovs = img_left < left or img_left + img_width > left + width
|
|
529
|
+
y_ovs = img_top < top or img_top + img_height > top + height
|
|
530
|
+
|
|
531
|
+
# window extends beyond the visible frame on both axes: the simple calculation
|
|
532
|
+
# below only supports prescan/overscan on one axis
|
|
533
|
+
if x_ovs and y_ovs:
|
|
534
|
+
log.warning(
|
|
535
|
+
"Cannot compute BIASSEC/TRIMSEC: window %dx%d at %d,%d has prescan/overscan on both axes "
|
|
536
|
+
"(only one axis is supported).",
|
|
537
|
+
img_width,
|
|
538
|
+
img_height,
|
|
539
|
+
img_left,
|
|
540
|
+
img_top,
|
|
541
|
+
)
|
|
542
|
+
return
|
|
543
|
+
|
|
544
|
+
# window lies fully inside the visible frame: there is no prescan/overscan at all
|
|
545
|
+
if (
|
|
546
|
+
not x_ovs
|
|
547
|
+
and not y_ovs
|
|
548
|
+
and (
|
|
549
|
+
img_left > left
|
|
550
|
+
or img_left + img_width < left + width
|
|
551
|
+
or img_top > top
|
|
552
|
+
or img_top + img_height < top + height
|
|
553
|
+
)
|
|
554
|
+
):
|
|
555
|
+
log.info(
|
|
556
|
+
"No BIASSEC/TRIMSEC: window %dx%d at %d,%d lies fully inside the visible frame "
|
|
557
|
+
"(no prescan/overscan).",
|
|
558
|
+
img_width,
|
|
559
|
+
img_height,
|
|
560
|
+
img_left,
|
|
561
|
+
img_top,
|
|
562
|
+
)
|
|
530
563
|
return
|
|
531
564
|
|
|
532
565
|
# comments
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import hashlib
|
|
3
|
+
import hmac
|
|
2
4
|
import io
|
|
3
5
|
import json
|
|
4
6
|
import logging
|
|
@@ -33,12 +35,18 @@ INDEX_HTML = """
|
|
|
33
35
|
<title>Title</title>
|
|
34
36
|
</head>
|
|
35
37
|
<body>
|
|
36
|
-
<img src="
|
|
38
|
+
<img src="video.mjpg" width="100%">
|
|
37
39
|
</body>
|
|
38
40
|
</html>
|
|
39
41
|
|
|
40
42
|
"""
|
|
41
43
|
|
|
44
|
+
# session cookie for browser login (see _check_auth); only used when a token is configured
|
|
45
|
+
_COOKIE_NAME = "pyobs_video_session"
|
|
46
|
+
_COOKIE_LIFETIME = 24 * 60 * 60 # 24 h, in seconds
|
|
47
|
+
# delay on a wrong login password, to slow brute force (compare is constant-time either way)
|
|
48
|
+
_LOGIN_FAILURE_SLEEP = 1.0
|
|
49
|
+
|
|
42
50
|
|
|
43
51
|
async def calc_expose_timeout(webcam: IExposureTime, *args: Any, **kwargs: Any) -> float:
|
|
44
52
|
"""Calculates timeout for grabe_image()."""
|
|
@@ -71,7 +79,14 @@ class LastImage(NamedTuple):
|
|
|
71
79
|
|
|
72
80
|
|
|
73
81
|
class BaseVideo(Module, ImageFitsHeaderMixin, IVideo, IImageType, metaclass=ABCMeta):
|
|
74
|
-
"""Base class for all webcam modules.
|
|
82
|
+
"""Base class for all webcam modules.
|
|
83
|
+
|
|
84
|
+
The built-in HTTP server serves the MJPEG live view, the raw frame stream and cached FITS
|
|
85
|
+
images. Set the ``token`` parameter to protect all of them: machine clients send
|
|
86
|
+
``Authorization: Bearer <token>`` (as :class:`pyobs.vfs.HttpFile` does), browsers log in once
|
|
87
|
+
at ``/login`` and get an HMAC-signed session cookie that rides the same-origin ``<img>``
|
|
88
|
+
requests. Without a token (the default), no auth is enforced.
|
|
89
|
+
"""
|
|
75
90
|
|
|
76
91
|
__module__ = "pyobs.modules.camera"
|
|
77
92
|
|
|
@@ -90,6 +105,7 @@ class BaseVideo(Module, ImageFitsHeaderMixin, IVideo, IImageType, metaclass=ABCM
|
|
|
90
105
|
flip: bool = False,
|
|
91
106
|
sleep_time: int = 60,
|
|
92
107
|
fits_header_timeout: float = 15.0,
|
|
108
|
+
token: str | None = None,
|
|
93
109
|
**kwargs: Any,
|
|
94
110
|
):
|
|
95
111
|
"""Creates a new BaseWebcam.
|
|
@@ -112,6 +128,8 @@ class BaseVideo(Module, ImageFitsHeaderMixin, IVideo, IImageType, metaclass=ABCM
|
|
|
112
128
|
flip: Whether to flip around Y axis.
|
|
113
129
|
sleep_time: Time in s with inactivity after which the camera should go to sleep.
|
|
114
130
|
fits_header_timeout: Maximum seconds to wait for a peer's FITS headers before skipping them.
|
|
131
|
+
token: Shared secret required in the "Authorization: Bearer <token>" header (or a
|
|
132
|
+
login-page cookie) for stream and data access. If None (default), no auth is enforced.
|
|
115
133
|
"""
|
|
116
134
|
super().__init__(
|
|
117
135
|
fits_namespaces=fits_namespaces,
|
|
@@ -153,12 +171,23 @@ class BaseVideo(Module, ImageFitsHeaderMixin, IVideo, IImageType, metaclass=ABCM
|
|
|
153
171
|
self._cache = DataCache(cache_size)
|
|
154
172
|
|
|
155
173
|
# define web server
|
|
174
|
+
self._token = token
|
|
175
|
+
# serializes /login attempts so _LOGIN_FAILURE_SLEEP actually caps the guess rate;
|
|
176
|
+
# without it, concurrent connections each sleep independently and the rate scales
|
|
177
|
+
# with concurrency instead of being capped
|
|
178
|
+
self._login_lock = asyncio.Lock()
|
|
156
179
|
self._app = web.Application()
|
|
157
180
|
routes = [web.get("/ping", self.ping_handler), web.get("/{filename}", self.image_handler)]
|
|
158
181
|
if self._video_path is not None:
|
|
159
182
|
routes += [web.get("/", self.web_handler), web.get("/video.mjpg", self.video_handler)]
|
|
160
183
|
if self._raw_path is not None:
|
|
161
184
|
routes.append(web.get("/video.raw", self.raw_handler))
|
|
185
|
+
if self._token is not None:
|
|
186
|
+
routes += [
|
|
187
|
+
web.get("/login", self.login_handler),
|
|
188
|
+
web.post("/login", self.login_post_handler),
|
|
189
|
+
web.get("/logout", self.logout_handler),
|
|
190
|
+
]
|
|
162
191
|
self._app.add_routes(routes)
|
|
163
192
|
self._runner = web.AppRunner(self._app)
|
|
164
193
|
self._site: web.TCPSite | None = None
|
|
@@ -196,6 +225,165 @@ class BaseVideo(Module, ImageFitsHeaderMixin, IVideo, IImageType, metaclass=ABCM
|
|
|
196
225
|
"""Whether the server is started."""
|
|
197
226
|
return self._is_listening
|
|
198
227
|
|
|
228
|
+
def _sign(self, expiry: int) -> str:
|
|
229
|
+
"""HMAC-SHA256 of ``expiry``, keyed with the configured token, as a hex digest.
|
|
230
|
+
|
|
231
|
+
Shared by _make_session_value (signs) and _check_cookie (verifies), so the two can't
|
|
232
|
+
drift apart on hash algo/encoding.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
expiry: Session expiry timestamp being signed.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Hex-encoded HMAC signature.
|
|
239
|
+
"""
|
|
240
|
+
token = self._token
|
|
241
|
+
if token is None:
|
|
242
|
+
raise ValueError("Cannot sign a session without a configured token.")
|
|
243
|
+
return hmac.new(token.encode(), str(expiry).encode(), hashlib.sha256).hexdigest()
|
|
244
|
+
|
|
245
|
+
def _make_session_value(self) -> str:
|
|
246
|
+
"""Build the value for the login session cookie.
|
|
247
|
+
|
|
248
|
+
Stateless and HMAC-signed -- the cookie does not contain the token itself:
|
|
249
|
+
|
|
250
|
+
``value = "{expiry_ts}.{hex}"`` where ``hex = HMAC-SHA256(key=token, msg=str(expiry_ts))``
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Cookie value.
|
|
254
|
+
"""
|
|
255
|
+
expiry = int(time.time()) + _COOKIE_LIFETIME
|
|
256
|
+
return f"{expiry}.{self._sign(expiry)}"
|
|
257
|
+
|
|
258
|
+
def _check_bearer(self, request: web.Request) -> bool:
|
|
259
|
+
"""Whether the request carries the token as "Authorization: Bearer <token>".
|
|
260
|
+
|
|
261
|
+
Only called (via _check_auth) once a token is configured.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
request: Request to check.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
True if the header matches the configured token.
|
|
268
|
+
"""
|
|
269
|
+
prefix = "Bearer "
|
|
270
|
+
header = request.headers.get("Authorization", "")
|
|
271
|
+
if not header.startswith(prefix):
|
|
272
|
+
return False
|
|
273
|
+
return hmac.compare_digest(header[len(prefix) :], self._token or "")
|
|
274
|
+
|
|
275
|
+
def _check_cookie(self, request: web.Request) -> bool:
|
|
276
|
+
"""Whether the request carries a valid, unexpired session cookie.
|
|
277
|
+
|
|
278
|
+
Only called (via _check_auth) once a token is configured.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
request: Request to check.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
True if the cookie's HMAC signature verifies and it has not expired.
|
|
285
|
+
"""
|
|
286
|
+
value = request.cookies.get(_COOKIE_NAME)
|
|
287
|
+
if value is None:
|
|
288
|
+
return False
|
|
289
|
+
try:
|
|
290
|
+
expiry_str, signature = value.rsplit(".", 1)
|
|
291
|
+
expiry = int(expiry_str)
|
|
292
|
+
except ValueError:
|
|
293
|
+
return False
|
|
294
|
+
if expiry < time.time():
|
|
295
|
+
return False
|
|
296
|
+
return hmac.compare_digest(signature, self._sign(expiry))
|
|
297
|
+
|
|
298
|
+
def _check_auth(self, request: web.Request) -> None:
|
|
299
|
+
"""Raises HTTPUnauthorized if a token is configured and the request doesn't carry it.
|
|
300
|
+
|
|
301
|
+
Accepts either a valid "Authorization: Bearer <token>" header or a valid session cookie.
|
|
302
|
+
No-op when no token is configured. Same contract as HttpFileCache._check_auth: the
|
|
303
|
+
exception type is identical regardless of which handler called it.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
request: Request to check.
|
|
307
|
+
"""
|
|
308
|
+
if self._token is None:
|
|
309
|
+
return
|
|
310
|
+
if not (self._check_bearer(request) or self._check_cookie(request)):
|
|
311
|
+
raise web.HTTPUnauthorized()
|
|
312
|
+
|
|
313
|
+
async def login_handler(self, request: web.Request) -> web.Response:
|
|
314
|
+
"""Handles GET access to /login and returns the login form.
|
|
315
|
+
|
|
316
|
+
Served unauthenticated: this is the bootstrap a browser needs, since it cannot attach an
|
|
317
|
+
"Authorization" header to an <img> request. Only registered when a token is configured.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
request: Request to respond to.
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
Response containing the login form.
|
|
324
|
+
"""
|
|
325
|
+
html = """<!DOCTYPE html>
|
|
326
|
+
<html lang="en">
|
|
327
|
+
<head>
|
|
328
|
+
<meta charset="UTF-8">
|
|
329
|
+
<title>Login</title>
|
|
330
|
+
</head>
|
|
331
|
+
<body>
|
|
332
|
+
<form method="post" action="/login">
|
|
333
|
+
<label>Password: <input type="password" name="token" autofocus></label>
|
|
334
|
+
<button type="submit">Login</button>
|
|
335
|
+
</form>
|
|
336
|
+
</body>
|
|
337
|
+
</html>
|
|
338
|
+
"""
|
|
339
|
+
return web.Response(text=html, content_type="text/html")
|
|
340
|
+
|
|
341
|
+
async def login_post_handler(self, request: web.Request) -> web.Response:
|
|
342
|
+
"""Handles POST access to /login, verifying the password and issuing the session cookie.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
request: Request to respond to.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
303 redirect to / on success, 401 on wrong password.
|
|
349
|
+
"""
|
|
350
|
+
token = self._token
|
|
351
|
+
if token is None:
|
|
352
|
+
# login routes are only registered when a token is configured -- defensive
|
|
353
|
+
raise web.HTTPUnauthorized()
|
|
354
|
+
data = await request.post()
|
|
355
|
+
password = str(data.get("token", ""))
|
|
356
|
+
async with self._login_lock:
|
|
357
|
+
# serialized: caps the guess rate at 1/_LOGIN_FAILURE_SLEEP regardless of how many
|
|
358
|
+
# connections attempt concurrently
|
|
359
|
+
if not hmac.compare_digest(password, token):
|
|
360
|
+
# the compare above is constant-time; this sleep additionally slows brute force
|
|
361
|
+
await asyncio.sleep(_LOGIN_FAILURE_SLEEP)
|
|
362
|
+
raise web.HTTPUnauthorized()
|
|
363
|
+
response = web.HTTPSeeOther("/")
|
|
364
|
+
response.set_cookie(
|
|
365
|
+
_COOKIE_NAME,
|
|
366
|
+
self._make_session_value(),
|
|
367
|
+
max_age=_COOKIE_LIFETIME,
|
|
368
|
+
path="/",
|
|
369
|
+
httponly=True,
|
|
370
|
+
samesite="Lax",
|
|
371
|
+
)
|
|
372
|
+
return response
|
|
373
|
+
|
|
374
|
+
async def logout_handler(self, request: web.Request) -> web.Response:
|
|
375
|
+
"""Handles GET access to /logout and clears the session cookie.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
request: Request to respond to.
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
303 redirect to /login.
|
|
382
|
+
"""
|
|
383
|
+
response = web.HTTPSeeOther("/login")
|
|
384
|
+
response.del_cookie(_COOKIE_NAME, path="/")
|
|
385
|
+
return response
|
|
386
|
+
|
|
199
387
|
async def web_handler(self, request: web.Request) -> web.Response:
|
|
200
388
|
"""Handles access to / and returns HTML page.
|
|
201
389
|
|
|
@@ -205,6 +393,11 @@ class BaseVideo(Module, ImageFitsHeaderMixin, IVideo, IImageType, metaclass=ABCM
|
|
|
205
393
|
Returns:
|
|
206
394
|
Response containing web page.
|
|
207
395
|
"""
|
|
396
|
+
# a browser landing on / should be taken to the login form, not shown a bare 401
|
|
397
|
+
try:
|
|
398
|
+
self._check_auth(request)
|
|
399
|
+
except web.HTTPUnauthorized:
|
|
400
|
+
raise web.HTTPSeeOther("/login")
|
|
208
401
|
return web.Response(text=INDEX_HTML, content_type="text/html")
|
|
209
402
|
|
|
210
403
|
async def ping_handler(self, request: web.Request) -> web.Response:
|
|
@@ -227,6 +420,7 @@ class BaseVideo(Module, ImageFitsHeaderMixin, IVideo, IImageType, metaclass=ABCM
|
|
|
227
420
|
Returns:
|
|
228
421
|
Response containing video stream.
|
|
229
422
|
"""
|
|
423
|
+
self._check_auth(request)
|
|
230
424
|
|
|
231
425
|
# create response
|
|
232
426
|
response = web.StreamResponse()
|
|
@@ -273,6 +467,8 @@ class BaseVideo(Module, ImageFitsHeaderMixin, IVideo, IImageType, metaclass=ABCM
|
|
|
273
467
|
Returns:
|
|
274
468
|
Response containing raw frame stream.
|
|
275
469
|
"""
|
|
470
|
+
# auth first: an unauthenticated request must not wake the camera
|
|
471
|
+
self._check_auth(request)
|
|
276
472
|
|
|
277
473
|
# activate camera
|
|
278
474
|
await self.activate_camera()
|
|
@@ -368,6 +564,7 @@ class BaseVideo(Module, ImageFitsHeaderMixin, IVideo, IImageType, metaclass=ABCM
|
|
|
368
564
|
Returns:
|
|
369
565
|
Response containing image.
|
|
370
566
|
"""
|
|
567
|
+
self._check_auth(request)
|
|
371
568
|
|
|
372
569
|
# get filename
|
|
373
570
|
filename = request.match_info["filename"]
|
|
@@ -29,6 +29,12 @@ class ImageWatcher(Module):
|
|
|
29
29
|
|
|
30
30
|
Watches a path for new files and stores them in all given destinations. Only if all operations were successful,
|
|
31
31
|
the file is deleted.
|
|
32
|
+
|
|
33
|
+
New files are not processed immediately, but only after a wait time (``wait_time``) has passed since they were
|
|
34
|
+
detected. This is mainly to make sure the file is fully written before it is read: while the inotify watcher
|
|
35
|
+
only reports files after they have been closed for writing (``CLOSE_WRITE``), the polling watcher and the
|
|
36
|
+
initial scan in ``open()`` can see a file while its write is still in progress. The same wait time is also
|
|
37
|
+
used to delay re-processing of files whose destination copy failed (see ``_worker``).
|
|
32
38
|
"""
|
|
33
39
|
|
|
34
40
|
__module__ = "pyobs.modules.image"
|
|
@@ -50,7 +56,9 @@ class ImageWatcher(Module):
|
|
|
50
56
|
destinations: Filename patterns for destinations.
|
|
51
57
|
poll: If True, watchpath is polled instead of watched by inotify.
|
|
52
58
|
poll_interval: Interval for polling in seconds, if poll is True.
|
|
53
|
-
wait_time: Time in seconds between adding file to list and processing it.
|
|
59
|
+
wait_time: Time in seconds between adding a file to the list and processing it. Gives a file that is
|
|
60
|
+
still being written time to finish (relevant for poll mode and the initial scan) and spaces out
|
|
61
|
+
re-queued files after failed destination copies.
|
|
54
62
|
"""
|
|
55
63
|
Module.__init__(self, **kwargs)
|
|
56
64
|
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/robotic/utils/skyflats/flatfielder.py
RENAMED
|
@@ -489,7 +489,7 @@ class FlatFielder(Object):
|
|
|
489
489
|
|
|
490
490
|
# log and return
|
|
491
491
|
if frac > self._allowed_offset_frac:
|
|
492
|
-
log.
|
|
492
|
+
log.info("Deviation from target count (%.1f%%) is larger than allowed, retrying last image...", frac)
|
|
493
493
|
return False
|
|
494
494
|
else:
|
|
495
495
|
log.info("Calculated new exposure time to be %.2fs.", self._exptime)
|
|
@@ -78,6 +78,14 @@ class HttpFile(BufferedFile):
|
|
|
78
78
|
raise ValueError("No download URL given.")
|
|
79
79
|
return urljoin(self._download_path, self.filename)
|
|
80
80
|
|
|
81
|
+
@property
|
|
82
|
+
def headers(self) -> dict[str, str]:
|
|
83
|
+
"""Headers sent with GET/POST requests, e.g. "Authorization: Bearer <token>".
|
|
84
|
+
|
|
85
|
+
Returns a copy; mutating it does not affect this instance's headers.
|
|
86
|
+
"""
|
|
87
|
+
return dict(self._headers)
|
|
88
|
+
|
|
81
89
|
async def _download(self) -> None:
|
|
82
90
|
"""For read access, download the file into a local buffer.
|
|
83
91
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xep_0009_timeout/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/comm/xmpp/xep_0009_timeout/stanza/RPC.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/_daobackgroundremover.py
RENAMED
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/annotation/__init__.py
RENAMED
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/annotation/_pillow.py
RENAMED
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/annotation/circle.py
RENAMED
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/annotation/crosshair.py
RENAMED
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/annotation/text.py
RENAMED
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/astrometry/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/astrometry/astrometry.py
RENAMED
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/astrometry/dotnet.py
RENAMED
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/calibration/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyobs_core-2.0.0.dev90 → pyobs_core-2.0.0.dev92}/pyobs/images/processors/calibration/calibration.py
RENAMED
|
File without changes
|