Initial commit - Support for Get Hydrometer and Get Fermentation Chamber
This commit is contained in:
commit
df1be4c144
162
.gitignore
vendored
Normal file
162
.gitignore
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
# ---> Python
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#t
|
||||
Pipfile.lock
|
16
Pipfile
Normal file
16
Pipfile
Normal file
@ -0,0 +1,16 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
requests = "*"
|
||||
pydantic = "*"
|
||||
loguru = "*"
|
||||
|
||||
[dev-packages]
|
||||
pytest = "*"
|
||||
responses = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.13"
|
4
pytest.ini
Normal file
4
pytest.ini
Normal file
@ -0,0 +1,4 @@
|
||||
[pytest]
|
||||
pythonpath = src
|
||||
testpaths = tests
|
||||
addopts = -s
|
10
src/live_test_client.py
Normal file
10
src/live_test_client.py
Normal file
@ -0,0 +1,10 @@
|
||||
from rapt.client import Client
|
||||
from rapt.hydrometer import Hydrometer
|
||||
|
||||
client = Client("jfm@moerks.dk", "9OBkd5yv1xY8")
|
||||
hydrometer = Hydrometer(client)
|
||||
|
||||
#hydrometer.get_hydrometers()
|
||||
#hydrometer.get_hydrometer("2aa3b02c-78de-4715-8f5b-61bf7c3d1b62")
|
||||
hydrometer.get_telemetry("2aa3b02c-78de-4715-8f5b-61bf7c3d1b62", "2025-10-07T12:51:07.3321557+00:00", "2025-10-08T12:51:07.3321557+00:00", "")
|
||||
|
0
src/rapt/__init__.py
Normal file
0
src/rapt/__init__.py
Normal file
65
src/rapt/client.py
Normal file
65
src/rapt/client.py
Normal file
@ -0,0 +1,65 @@
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class ClientException(Exception):
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
class HttpClientException(ClientException):
|
||||
def __init__(self, message, http_code):
|
||||
self.message = message
|
||||
self.http_code = http_code
|
||||
logger.error(str(self))
|
||||
|
||||
def __str__(self):
|
||||
return "["+str(self.http_code)+"] - " + self.message
|
||||
|
||||
|
||||
|
||||
class Client():
|
||||
def __init__(self, username, api_key) -> None:
|
||||
self.host = "https://api.rapt.io"
|
||||
self.username = username
|
||||
self.api_key = api_key
|
||||
self.token = None
|
||||
self.expires_seconds = None
|
||||
self.expires = None
|
||||
|
||||
def get_jwt_token(self):
|
||||
url = "https://id.rapt.io/connect/token"
|
||||
response = requests.post(url, data={
|
||||
"client_id": "rapt-user",
|
||||
"grant_type": "password",
|
||||
"username": self.username,
|
||||
"password": self.api_key
|
||||
})
|
||||
|
||||
if response.status_code == 200:
|
||||
response_json = response.json()
|
||||
self.token = response_json["access_token"]
|
||||
self.expires_seconds = response_json["expires_in"]
|
||||
self.expires = datetime.now() + timedelta(seconds=self.expires_seconds)
|
||||
logger.trace("Expires: " + str(self.expires))
|
||||
else:
|
||||
raise HttpClientException(response.reason, response.status_code)
|
||||
|
||||
def get_auth_headers(self):
|
||||
#TODO: Check expiry
|
||||
if self.expires is None or self.expires <= datetime.now():
|
||||
self.get_jwt_token()
|
||||
|
||||
return {"Authorization": "Bearer {}".format(self.token)}
|
||||
|
||||
def get_json(self, url, parameters):
|
||||
headers = self.get_auth_headers()
|
||||
response = requests.get(self.host+url, headers=headers, params=parameters)
|
||||
logger.trace(response.request.url)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
raise HttpClientException(response.reason, response.status_code)
|
53
src/rapt/fermentation_chamber.py
Normal file
53
src/rapt/fermentation_chamber.py
Normal file
@ -0,0 +1,53 @@
|
||||
from rapt.client import ClientException
|
||||
from rapt.model.fermentation_chamber import FermentationChamberModel, FermentationChamberTelemetryModel
|
||||
from typing import Optional, List
|
||||
from pydantic import TypeAdapter
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class FermentationChamber():
|
||||
def __init__(self, client) -> None:
|
||||
self.client = client
|
||||
|
||||
def get_fermentation_chambers(self) -> Optional[List[FermentationChamberModel]]:
|
||||
logger.debug("## GET FERMENTATION CHAMBERS ##")
|
||||
try:
|
||||
response_json = self.client.get_json("/api/fermentationchambers/getfermentationchambers", None)
|
||||
logger.trace(response_json)
|
||||
fermentation_chambers_adapter = TypeAdapter(list[FermentationChamberModel])
|
||||
return fermentation_chambers_adapter.validate_python(response_json)
|
||||
except ClientException:
|
||||
#TODO: Handle Exception gracefully
|
||||
return None
|
||||
|
||||
def get_fermentation_chamber(self, fermentation_chamber_id) -> Optional[FermentationChamberModel]:
|
||||
logger.debug("## GET FERMENTATION CHAMBER ##")
|
||||
params = {
|
||||
"fermentationChamberId": fermentation_chamber_id
|
||||
}
|
||||
try:
|
||||
response_json = self.client.get_json("/api/fermentationchambers/getfermentationchamber", params)
|
||||
logger.trace(response_json)
|
||||
return FermentationChamberModel.model_validate(response_json)
|
||||
except ClientException:
|
||||
#TODO: Handle Exception gracefully
|
||||
return None
|
||||
|
||||
|
||||
def get_telemetry(self, fermentation_chamber_id, start_date, end_date, profile_session_id) -> Optional[List[FermentationChamberTelemetryModel]]:
|
||||
logger.debug("## GET TELEMETRY ##")
|
||||
params = {
|
||||
"fermentationChamberId": fermentation_chamber_id,
|
||||
"startDate": start_date,
|
||||
"endDate": end_date,
|
||||
"profileSessionId": profile_session_id
|
||||
}
|
||||
try:
|
||||
response_json = self.client.get_json("/api/fermentationchambers/gettelemetry", params)
|
||||
logger.trace(response_json)
|
||||
fermentation_chambers_adapter = TypeAdapter(list[FermentationChamberTelemetryModel])
|
||||
return fermentation_chambers_adapter.validate_python(response_json)
|
||||
except ClientException:
|
||||
#TODO: Handle Exception gracefully
|
||||
return None
|
||||
|
53
src/rapt/hydrometer.py
Normal file
53
src/rapt/hydrometer.py
Normal file
@ -0,0 +1,53 @@
|
||||
from rapt.client import ClientException
|
||||
from rapt.model.hydrometer import HydrometerModel, HydrometerTelemetryModel
|
||||
from pydantic import TypeAdapter
|
||||
from typing import Optional, List
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class Hydrometer():
|
||||
def __init__(self, client) -> None:
|
||||
self.client = client
|
||||
|
||||
def get_hydrometers(self) -> Optional[List[HydrometerModel]]:
|
||||
logger.debug("## GET HYDROMETERS ##")
|
||||
try:
|
||||
response_json = self.client.get_json("/api/hydrometers/gethydrometers", None)
|
||||
logger.trace(response_json)
|
||||
hydrometers_adapter = TypeAdapter(list[HydrometerModel])
|
||||
return hydrometers_adapter.validate_python(response_json)
|
||||
except ClientException:
|
||||
#TODO: Handle Exception gracefully
|
||||
return None
|
||||
|
||||
def get_hydrometer(self, hydrometer_id) -> Optional[HydrometerModel]:
|
||||
logger.debug("## GET HYDROMETER ##")
|
||||
params = {
|
||||
"hydrometerId": hydrometer_id
|
||||
}
|
||||
try:
|
||||
response_json = self.client.get_json("/api/hydrometers/gethydrometer", params)
|
||||
logger.trace(response_json)
|
||||
return HydrometerModel.model_validate(response_json)
|
||||
except ClientException:
|
||||
#TODO: Handle Exception gracefully
|
||||
return None
|
||||
|
||||
|
||||
def get_telemetry(self, hydrometer_id, start_date, end_date, profile_session_id) -> Optional[List[HydrometerTelemetryModel]]:
|
||||
logger.debug("## GET TELEMETRY ##")
|
||||
params = {
|
||||
"hydrometerId": hydrometer_id,
|
||||
"startDate": start_date,
|
||||
"endDate": end_date,
|
||||
"profileSessionId": profile_session_id
|
||||
}
|
||||
try:
|
||||
response_json = self.client.get_json("/api/hydrometers/gettelemetry", params)
|
||||
logger.trace(response_json)
|
||||
hydrometers_adapter = TypeAdapter(list[HydrometerTelemetryModel])
|
||||
return hydrometers_adapter.validate_python(response_json)
|
||||
except ClientException:
|
||||
#TODO: Handle Exception gracefully
|
||||
return None
|
||||
|
0
src/rapt/model/__init__.py
Normal file
0
src/rapt/model/__init__.py
Normal file
6
src/rapt/model/amount.py
Normal file
6
src/rapt/model/amount.py
Normal file
@ -0,0 +1,6 @@
|
||||
from enum import Enum
|
||||
|
||||
class AmountTypes(str, Enum):
|
||||
Weight = "Weight",
|
||||
Volume = "Volume",
|
||||
Units = "Units"
|
13
src/rapt/model/base.py
Normal file
13
src/rapt/model/base.py
Normal file
@ -0,0 +1,13 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
class RaptBaseModel(BaseModel):
|
||||
id: UUID
|
||||
deleted: bool
|
||||
created_on: datetime = Field(alias="createdOn")
|
||||
created_by: UUID = Field(alias="createdBy")
|
||||
modified_on: datetime = Field(alias="modifiedOn")
|
||||
modified_by: UUID = Field(alias="modifiedBy")
|
||||
|
39
src/rapt/model/device.py
Normal file
39
src/rapt/model/device.py
Normal file
@ -0,0 +1,39 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class DeviceTypes(str, Enum):
|
||||
FermentationChamber = "FermentationChamber",
|
||||
TemperatureController = "TemperatureController",
|
||||
Hydrometer = "Hydrometer",
|
||||
GrainWeigh = "GrainWeigh",
|
||||
BrewZilla = "BrewZilla",
|
||||
CanFiller = "CanFiller",
|
||||
GlycolChiller = "GlycolChiller",
|
||||
Still = "Still",
|
||||
BLETemperature = "BLETemperature",
|
||||
BLEHumidity = "BLEHumidity",
|
||||
BLETempHumidity = "BLETempHumidity",
|
||||
BLEPressure = "BLEPressure",
|
||||
External = "External",
|
||||
Fridge = "Fridge",
|
||||
GrainWeighDevice = "GrainWeighDevice",
|
||||
Unknown = "Unknown"
|
||||
|
||||
class DeviceTypesNullable(str, Enum):
|
||||
FermentationChamber = "FermentationChamber",
|
||||
TemperatureController = "TemperatureController",
|
||||
Hydrometer = "Hydrometer",
|
||||
GrainWeigh = "GrainWeigh",
|
||||
BrewZilla = "BrewZilla",
|
||||
CanFiller = "CanFiller",
|
||||
GlycolChiller = "GlycolChiller",
|
||||
Still = "Still",
|
||||
BLETemperature = "BLETemperature",
|
||||
BLEHumidity = "BLEHumidity",
|
||||
BLETempHumidity = "BLETempHumidity",
|
||||
BLEPressure = "BLEPressure",
|
||||
External = "External",
|
||||
Fridge = "Fridge",
|
||||
GrainWeighDevice = "GrainWeighDevice",
|
||||
Unknown = "Unknown"
|
||||
null = "null"
|
102
src/rapt/model/fermentation_chamber.py
Normal file
102
src/rapt/model/fermentation_chamber.py
Normal file
@ -0,0 +1,102 @@
|
||||
from rapt.model.base import RaptBaseModel
|
||||
from rapt.model.profile import ProfileSessionModel, ProfileSessionStatusModel
|
||||
from rapt.model.device import DeviceTypes
|
||||
from rapt.model.toggle import ToggleStates
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List
|
||||
|
||||
|
||||
class FermentationChamberTelemetryModel(BaseModel):
|
||||
id: UUID
|
||||
row_key: Optional[str] = Field(alias="rowKey", default=None)
|
||||
created_on: datetime = Field(alias="createdOn")
|
||||
mac_address: Optional[str] = Field(alias="macAddress", default=None)
|
||||
rssi: float
|
||||
control_device_type: Optional[str] = Field(alias="controlDeviceType", default=None)
|
||||
control_device_mac_address: Optional[str] = Field(alias="controlDeviceMacAddress", default=None)
|
||||
control_device_temperature: Optional[float] = Field(alias="controlDeviceTemperature", default=None)
|
||||
temperature: float
|
||||
target_temperature: Optional[float] = Field(alias="targetTemperature", default=None)
|
||||
min_target_temperature: Optional[float] = Field(alias="minTargetTemperature", default=None)
|
||||
max_target_temperature: Optional[float] = Field(alias="maxTargetTemperature", default=None)
|
||||
total_run_time: float = Field(alias="totalRunTime")
|
||||
compressor_run_time: float = Field(alias="compressorRunTime")
|
||||
compressor_starts: float = Field(alias="compressorStarts")
|
||||
heating_run_time: float = Field(alias="heatingRunTime")
|
||||
heating_starts: float = Field(alias="heatingStarts")
|
||||
auxillary_run_time: float = Field(alias="auxillaryRunTime")
|
||||
auxillary_starts: float = Field(alias="auxillaryStarts")
|
||||
profile_id: Optional[UUID] = Field(alias="profileId", default=None)
|
||||
profile_step_id: Optional[UUID] = Field(alias="profileStepId", default=None)
|
||||
profile_session_start_date: Optional[datetime] = Field(alias="profileSessionStartDate", default=None)
|
||||
profile_session_time: Optional[int] = Field(alias="profileSessionTime", default=None)
|
||||
profile_step_progress: Optional[int] = Field(alias="profileStepProgress", default=None)
|
||||
|
||||
class FermentationChamberModel(RaptBaseModel):
|
||||
name: str
|
||||
serial_number: Optional[str] = Field(alias="serialNumber", default=None)
|
||||
mac_address: str = Field(alias="macAddress")
|
||||
device_type: DeviceTypes = Field(alias="deviceType")
|
||||
active: bool
|
||||
disabled: bool
|
||||
username: Optional[str] = None
|
||||
connection_state: Optional[str] = Field(alias="connectionState", default=None)
|
||||
status: Optional[str] = None
|
||||
error: Optional[str] = None
|
||||
last_activity_time: Optional[datetime] = Field(alias="lastActivityTime", default=None)
|
||||
rssi: float
|
||||
firmware_version: Optional[str] = Field(alias="firmwareVersion", default=None)
|
||||
is_latest_firmware: bool = Field(alias="isLatestFirmware")
|
||||
active_profile_id: Optional[UUID] = Field(alias="activeProfileId", default=None)
|
||||
active_profile_step_id: Optional[UUID] = Field(alias="activeProfileStepId", default=None)
|
||||
active_profile_session: ProfileSessionStatusModel = Field(alias="activeProfileSession")
|
||||
profile_sessions: Optional[List[ProfileSessionModel]] = Field(alias="profileSessions", default=None)
|
||||
beta_updates: bool = Field(alias="betaUpdates")
|
||||
bluetooth_enabled: bool = Field(alias="bluetoothEnabled")
|
||||
graph_zoom_level: float = Field(alias="graphZoomLevel")
|
||||
temperature: float
|
||||
target_temperature: Optional[float] = Field(alias="targetTemperature", default=None)
|
||||
min_target_temperature: Optional[float] = Field(alias="minTargetTemperature", default=None)
|
||||
max_target_temperature: Optional[float] = Field(alias="maxTargetTemperature", default=None)
|
||||
total_run_time: float = Field(alias="totalRunTime")
|
||||
cooling_enabled: bool = Field(alias="coolingEnabled")
|
||||
cooling_run_time: float = Field(alias="coolingRunTime")
|
||||
cooling_starts: float = Field(alias="coolingStarts")
|
||||
heating_enabled: bool = Field(alias="heatingEnabled")
|
||||
heating_run_time: float = Field(alias="heatingRunTime")
|
||||
heating_starts: float = Field(alias="heatingStarts")
|
||||
heating_utilisation: float = Field(alias="heatingUtilisation")
|
||||
high_temp_alarm: float = Field(alias="highTempAlarm")
|
||||
low_temp_alarm: float = Field(alias="lowTempAlarm")
|
||||
ntc_beta: float = Field(alias="ntcBeta")
|
||||
ntc_ref_resistance: float = Field(alias="ntcRefResistance")
|
||||
ntc_ref_temperature: float = Field(alias="ntcRefTemperature")
|
||||
pid_cycle_time: float = Field(alias="pidCycleTime")
|
||||
pid_enabled: bool = Field(alias="pidEnabled")
|
||||
pid_proportional: float = Field(alias="pidProportional")
|
||||
pid_integral: float = Field(alias="pidIntegral")
|
||||
pid_derivative: float = Field(alias="pidDerivative")
|
||||
sensor_differential: float = Field(alias="sensorDifferential")
|
||||
sensor_timeout: float = Field(alias="sensorTimeout")
|
||||
show_graph: bool = Field(alias="showGraph")
|
||||
sounds_enabled: bool = Field(alias="soundsEnabled")
|
||||
temp_unit: Optional[str] = Field(alias="tempUnit", default=None)
|
||||
use_internal_sensor: bool = Field(alias="useInternalSensor")
|
||||
control_device_type: Optional[str] = Field(alias="controlDeviceType", default=None)
|
||||
control_device_mac_address: Optional[str] = Field(alias="controlDeviceMacAddress", default=None)
|
||||
control_device_temperature: Optional[float] = Field(alias="controlDeviceTemperature", default=None)
|
||||
customer_use: str = Field(alias="customerUse")
|
||||
telemetry_frequency: int = Field(alias="telemetryFrequency")
|
||||
compressor_delay: float = Field(alias="compressorDelay")
|
||||
mode_switch_delay: float = Field(alias="modeSwitchDelay")
|
||||
cooling_hysteresis: float = Field(alias="coolingHysteresis")
|
||||
heating_hysteresis: float = Field(alias="heatingHysteresis")
|
||||
telemetry: Optional[List[FermentationChamberTelemetryModel]] = None
|
||||
compressor_run_time: float = Field(alias="compressorRunTime")
|
||||
compressor_starts: float = Field(alias="compressorStarts")
|
||||
auxillary_run_time: float = Field(alias="auxillaryRunTime")
|
||||
auxillary_starts: float = Field(alias="auxillaryStarts")
|
||||
fan_enabled: bool = Field(alias="fanEnabled")
|
||||
light_enabled: ToggleStates = Field(alias="lightEnabled")
|
46
src/rapt/model/hydrometer.py
Normal file
46
src/rapt/model/hydrometer.py
Normal file
@ -0,0 +1,46 @@
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List
|
||||
from rapt.model.base import RaptBaseModel
|
||||
from rapt.model.device import DeviceTypes, DeviceTypesNullable
|
||||
from rapt.model.profile import ProfileSessionStatusModel
|
||||
|
||||
|
||||
class HydrometerTelemetryModel(BaseModel):
|
||||
id: UUID
|
||||
row_key: Optional[str] = Field(alias="rowKey")
|
||||
created_on: datetime = Field(alias="createdOn")
|
||||
mac_address: Optional[str] = Field(alias="macAddress")
|
||||
rssi: float
|
||||
temperature: float
|
||||
gravity: float
|
||||
gravity_velocity: float = Field(alias="gravityVelocity")
|
||||
battery: float
|
||||
version: Optional[str]
|
||||
|
||||
class HydrometerModel(RaptBaseModel):
|
||||
name: str
|
||||
serial_number: Optional[str] = Field(alias="serialNumber", default=None)
|
||||
mac_address: str = Field(alias="macAddress")
|
||||
device_type: DeviceTypes = Field(alias="deviceType")
|
||||
active: bool
|
||||
disabled: bool
|
||||
username: Optional[str] = None
|
||||
connection_state: Optional[str] = Field(alias="connectionState", default=None)
|
||||
status: Optional[str] = None
|
||||
error: Optional[str] = None
|
||||
last_activity_time: Optional[datetime] = Field(alias="lastActivityTime")
|
||||
rssi: float
|
||||
firmware_version: Optional[str] = Field(alias="firmwareVersion")
|
||||
is_latest_firmware: bool = Field(alias="isLatestFirmware")
|
||||
active_profile_id: Optional[UUID] = Field(alias="activeProfileId", default=None)
|
||||
active_profile_step: Optional[UUID] = Field(alias="activeProfileStepId", default=None)
|
||||
active_profile_session: Optional[ProfileSessionStatusModel] = Field(alias="activeProfileSession", default=None)
|
||||
telemetry: Optional[List[HydrometerTelemetryModel]]
|
||||
paired_device_type: Optional[DeviceTypesNullable] = Field(alias="pairedDeviceType", default=None)
|
||||
paired_device_id: Optional[UUID] = Field(alias="pairedDeviceId", default=None)
|
||||
temperature: float
|
||||
gravity: float
|
||||
gravity_velocity: float = Field(alias="gravityVelocity")
|
||||
battery: float
|
134
src/rapt/model/profile.py
Normal file
134
src/rapt/model/profile.py
Normal file
@ -0,0 +1,134 @@
|
||||
from pydantic import Field
|
||||
from typing import Optional, List
|
||||
from enum import Enum
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from rapt.model.base import RaptBaseModel
|
||||
from rapt.model.yeast import YeastModel
|
||||
|
||||
|
||||
class ProfileAlertTriggers(str, Enum):
|
||||
StepStart = "StepStart",
|
||||
StepEnd = "StepEnd",
|
||||
Temperature = "Temperature",
|
||||
Gravity = "Gravity",
|
||||
GravityVelocity = "GravityVelocity",
|
||||
Duration = "Duration"
|
||||
|
||||
class ProfileValueOperatorsNullable(str, Enum):
|
||||
Equals = "Equals",
|
||||
LessThan = "LessThan",
|
||||
GreaterThan = "GreaterThan",
|
||||
null = "null"
|
||||
|
||||
class ProfileStepControlTypes(str, Enum):
|
||||
Target = "Target",
|
||||
Ramp = "Ramp",
|
||||
FreeRise = "FreeRise"
|
||||
|
||||
class ProfileStepEndTypes(str, Enum):
|
||||
Duration = "Duration",
|
||||
Temperature = "Temperature",
|
||||
Gravity = "Gravity",
|
||||
GravityVelocity = "GravityVelocity",
|
||||
Manual = "Manual"
|
||||
|
||||
class ProfileStepDurationTypesNullable(str, Enum):
|
||||
Start = "Start",
|
||||
Temperature = "Temperature",
|
||||
Gravity = "Gravity",
|
||||
GravityVelocity = "GravityVelocity",
|
||||
Manual = "Manual",
|
||||
null = "null"
|
||||
|
||||
class ProfileAlertModel(RaptBaseModel):
|
||||
alert_text: str = Field(alias="alertText")
|
||||
trigger: ProfileAlertTriggers
|
||||
operator: ProfileValueOperatorsNullable
|
||||
temperature: float
|
||||
gravity: float
|
||||
profile_id: UUID = Field(alias="profileId")
|
||||
|
||||
class ProfileStepAlertModel(RaptBaseModel):
|
||||
alert_text: Optional[str] = Field(alias="alertText")
|
||||
trigger: ProfileAlertTriggers
|
||||
operator: Optional[ProfileValueOperatorsNullable]
|
||||
temperature: Optional[float]
|
||||
gravity: Optional[float]
|
||||
length: Optional[int]
|
||||
profile_step_id: UUID = Field(alias="profileStepId")
|
||||
|
||||
class ProfileStepModel(RaptBaseModel):
|
||||
name: Optional[str]
|
||||
order: int
|
||||
control_type: ProfileStepControlTypes = Field(alias="controlType")
|
||||
end_type: ProfileStepEndTypes = Field(alias="endType")
|
||||
duration_type: Optional[ProfileStepDurationTypesNullable] = Field(alias="durationType")
|
||||
operator: Optional[ProfileValueOperatorsNullable]
|
||||
length: Optional[int]
|
||||
temperature: Optional[float]
|
||||
min_temperature: Optional[float] = Field(alias="minTemperature")
|
||||
max_temperature: Optional[float] = Field(alias="maxTemperature")
|
||||
gravity: Optional[float]
|
||||
pump_enabled: Optional[bool] = Field(alias="pumpEnabled")
|
||||
pump_utilisation: Optional[float] = Field(alias="pumpUtilisation")
|
||||
heating_utilisation: Optional[float] = Field(alias="heatingUtilisation")
|
||||
pid_enabled: Optional[bool] = Field(alias="pidEnabled")
|
||||
sensor_differential: Optional[float] = Field(alias="sensorDifferential")
|
||||
profile_id: UUID = Field(alias="profileId")
|
||||
alerts: Optional[List[ProfileStepAlertModel]]
|
||||
|
||||
class ProfileSessionModel(RaptBaseModel):
|
||||
name: Optional[str]
|
||||
description: Optional[str]
|
||||
profile_id: Optional[UUID] = Field(alias="profileId")
|
||||
# profile: ProfileModel
|
||||
brewzilla_id: Optional[UUID] = Field(alias="brewZillaId")
|
||||
fermentation_chamger_id: Optional[UUID] = Field(alias="fermentationChamberId")
|
||||
hydrometer_id: Optional[UUID] = Field(alias="hydrometerId")
|
||||
still_id: Optional[UUID] = Field(alias="stillId")
|
||||
temperature_controller_id: Optional[UUID] = Field(alias="temperatureControllerId")
|
||||
start_date: Optional[datetime] = Field(alias="startDate")
|
||||
end_date: Optional[datetime] = Field(alias="endDate")
|
||||
original_gravity: Optional[float] = Field(alias="originalGravity")
|
||||
final_gravity: Optional[float] = Field(alias="finalGravity")
|
||||
yeast_id: Optional[UUID] = Field(alias="yeastId")
|
||||
yeast: YeastModel
|
||||
sent_alerts: List[Optional[ProfileAlertModel]] = Field(alias="sentAlerts")
|
||||
|
||||
class ProfileModel(RaptBaseModel):
|
||||
name: str
|
||||
description: Optional[str]
|
||||
public: bool
|
||||
profile_name: Optional[str] = Field(alias="profileName")
|
||||
rating: float
|
||||
rating_count: int = Field(alias="ratingCount")
|
||||
rating_score: float = Field(alias="ratingScore")
|
||||
copy_count: float = Field(alias="copyCount")
|
||||
view_count: float = Field(alias="viewCount")
|
||||
profile_type_id: Optional[UUID] = Field(alias="profileTypeId")
|
||||
alerts: Optional[List[ProfileAlertModel]]
|
||||
steps: Optional[List[ProfileStepModel]]
|
||||
profile_sessions: Optional[List[ProfileSessionModel]] = Field(alias="profileSessions")
|
||||
|
||||
class ProfileSessionStatusModel(RaptBaseModel):
|
||||
name: Optional[str]
|
||||
description: Optional[str]
|
||||
profile_id: Optional[UUID] = Field(alias="profileId")
|
||||
profile: ProfileModel
|
||||
brewzilla_id: Optional[UUID] = Field(alias="brewZillaId")
|
||||
fermentation_chamger_id: Optional[UUID] = Field(alias="fermentationChamberId")
|
||||
hydrometer_id: Optional[UUID] = Field(alias="hydrometerId")
|
||||
still_id: Optional[UUID] = Field(alias="stillId")
|
||||
temperature_controller_id: Optional[UUID] = Field(alias="temperatureControllerId")
|
||||
start_date: Optional[datetime] = Field(alias="startDate")
|
||||
end_date: Optional[datetime] = Field(alias="endDate")
|
||||
original_gravity: Optional[float] = Field(alias="originalGravity")
|
||||
final_gravity: Optional[float] = Field(alias="finalGravity")
|
||||
yeast_id: Optional[UUID] = Field(alias="yeastId")
|
||||
yeast: YeastModel
|
||||
sent_alerts: List[Optional[ProfileAlertModel]] = Field(alias="sentAlerts")
|
||||
estimated_end_date: Optional[datetime] = Field(alias="estimatedEndDate")
|
||||
profile_length: Optional[float] = Field(alias="profileLength")
|
||||
current_profile_time: Optional[float] = Field(alias="currentProfileTime")
|
||||
remaining_profile_time: Optional[float] = Field(alias="remainingProfileTime")
|
6
src/rapt/model/toggle.py
Normal file
6
src/rapt/model/toggle.py
Normal file
@ -0,0 +1,6 @@
|
||||
from enum import Enum
|
||||
|
||||
class ToggleStates(str, Enum):
|
||||
AlwaysOn = "AlwaysOn",
|
||||
AlwaysOff = "AlwaysOff",
|
||||
Automatic = "Automatic"
|
29
src/rapt/model/yeast.py
Normal file
29
src/rapt/model/yeast.py
Normal file
@ -0,0 +1,29 @@
|
||||
from pydantic import Field
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
from rapt.model.base import RaptBaseModel
|
||||
from rapt.model.amount import AmountTypes
|
||||
|
||||
|
||||
class YeastModel(RaptBaseModel):
|
||||
name: Optional[str]
|
||||
product_id: Optional[str] = Field(alias="productId")
|
||||
laboratory: Optional[str]
|
||||
supplier: Optional[str]
|
||||
kegland_product_code: Optional[str] = Field(alias="keglandProductCode")
|
||||
yeast_type: Optional[str] = Field(alias="type")
|
||||
form: Optional[str]
|
||||
min_temperature: float = Field(alias="minTemperature")
|
||||
max_temperature: float = Field(alias="maxTemperature")
|
||||
flocculation: Optional[str]
|
||||
attenuation: float
|
||||
notes: Optional[str]
|
||||
best_for: Optional[str] = Field(alias="bestFor")
|
||||
max_reuse: int = Field(alias="maxReuse")
|
||||
add_to_secondary: bool = Field(alias="addToSecondary")
|
||||
amount_types: AmountTypes = Field(alias="amountType")
|
||||
inventory_amount: Optional[float] = Field(alias="inventoryAmount")
|
||||
archived: bool
|
||||
stock_level: float = Field(alias="stockLevel")
|
||||
global_yeast_id: Optional[UUID] = Field(alias="globalYeastId")
|
||||
is_global: bool = Field(alias="isGlobal")
|
384
tests/json/get_fermentation_chamber_response.json
Normal file
384
tests/json/get_fermentation_chamber_response.json
Normal file
@ -0,0 +1,384 @@
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"serialNumber": "string",
|
||||
"macAddress": "string",
|
||||
"deviceType": "FermentationChamber",
|
||||
"active": true,
|
||||
"disabled": true,
|
||||
"username": "string",
|
||||
"connectionState": "string",
|
||||
"status": "string",
|
||||
"error": "string",
|
||||
"lastActivityTime": "2025-10-09T11:56:29.302Z",
|
||||
"rssi": 0,
|
||||
"firmwareVersion": "string",
|
||||
"isLatestFirmware": true,
|
||||
"activeProfileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"activeProfileStepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"activeProfileSession": {
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profile": {
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"public": true,
|
||||
"profileName": "string",
|
||||
"rating": 0,
|
||||
"ratingCount": 0,
|
||||
"ratingScore": 0,
|
||||
"copyCount": 0,
|
||||
"viewCount": 0,
|
||||
"profileTypeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alerts": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alertText": "string",
|
||||
"trigger": "StepStart",
|
||||
"operator": "Equals",
|
||||
"temperature": 0,
|
||||
"gravity": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"order": 0,
|
||||
"controlType": "Target",
|
||||
"endType": "Duration",
|
||||
"durationType": "Start",
|
||||
"operator": "Equals",
|
||||
"length": 0,
|
||||
"temperature": 0,
|
||||
"minTemperature": 0,
|
||||
"maxTemperature": 0,
|
||||
"gravity": 0,
|
||||
"pumpEnabled": true,
|
||||
"pumpUtilisation": 0,
|
||||
"heatingUtilisation": 0,
|
||||
"pidEnabled": true,
|
||||
"sensorDifferential": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alerts": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alertText": "string",
|
||||
"trigger": "StepStart",
|
||||
"operator": "Equals",
|
||||
"temperature": 0,
|
||||
"gravity": 0,
|
||||
"length": 0,
|
||||
"profileStepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"profileSessions": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profile": "string",
|
||||
"brewZillaId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"fermentationChamberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"hydrometerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"stillId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"temperatureControllerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"startDate": "2025-10-09T11:56:29.302Z",
|
||||
"endDate": "2025-10-09T11:56:29.302Z",
|
||||
"originalGravity": 0,
|
||||
"finalGravity": 0,
|
||||
"yeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"yeast": {
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"productId": "string",
|
||||
"laboratory": "string",
|
||||
"supplier": "string",
|
||||
"keglandProductCode": "string",
|
||||
"type": "string",
|
||||
"form": "string",
|
||||
"minTemperature": 0,
|
||||
"maxTemperature": 0,
|
||||
"flocculation": "string",
|
||||
"attenuation": 0,
|
||||
"notes": "string",
|
||||
"bestFor": "string",
|
||||
"maxReuse": 0,
|
||||
"addToSecondary": true,
|
||||
"amountType": "Weight",
|
||||
"inventoryAmount": 0,
|
||||
"archived": true,
|
||||
"stockLevel": 0,
|
||||
"globalYeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"isGlobal": true
|
||||
},
|
||||
"sentAlerts": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alertText": "string",
|
||||
"trigger": "StepStart",
|
||||
"operator": "Equals",
|
||||
"temperature": 0,
|
||||
"gravity": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"brewZillaId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"fermentationChamberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"hydrometerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"stillId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"temperatureControllerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"startDate": "2025-10-09T11:56:29.302Z",
|
||||
"endDate": "2025-10-09T11:56:29.302Z",
|
||||
"originalGravity": 0,
|
||||
"finalGravity": 0,
|
||||
"yeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"yeast": {
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"productId": "string",
|
||||
"laboratory": "string",
|
||||
"supplier": "string",
|
||||
"keglandProductCode": "string",
|
||||
"type": "string",
|
||||
"form": "string",
|
||||
"minTemperature": 0,
|
||||
"maxTemperature": 0,
|
||||
"flocculation": "string",
|
||||
"attenuation": 0,
|
||||
"notes": "string",
|
||||
"bestFor": "string",
|
||||
"maxReuse": 0,
|
||||
"addToSecondary": true,
|
||||
"amountType": "Weight",
|
||||
"inventoryAmount": 0,
|
||||
"archived": true,
|
||||
"stockLevel": 0,
|
||||
"globalYeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"isGlobal": true
|
||||
},
|
||||
"sentAlerts": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alertText": "string",
|
||||
"trigger": "StepStart",
|
||||
"operator": "Equals",
|
||||
"temperature": 0,
|
||||
"gravity": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
],
|
||||
"estimatedEndDate": "2025-10-09T11:56:29.302Z",
|
||||
"profileLength": 0,
|
||||
"currentProfileTime": 0,
|
||||
"remainingProfileTime": 0
|
||||
},
|
||||
"profileSessions": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profile": "string",
|
||||
"brewZillaId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"fermentationChamberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"hydrometerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"stillId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"temperatureControllerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"startDate": "2025-10-09T11:56:29.302Z",
|
||||
"endDate": "2025-10-09T11:56:29.302Z",
|
||||
"originalGravity": 0,
|
||||
"finalGravity": 0,
|
||||
"yeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"yeast": {
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"productId": "string",
|
||||
"laboratory": "string",
|
||||
"supplier": "string",
|
||||
"keglandProductCode": "string",
|
||||
"type": "string",
|
||||
"form": "string",
|
||||
"minTemperature": 0,
|
||||
"maxTemperature": 0,
|
||||
"flocculation": "string",
|
||||
"attenuation": 0,
|
||||
"notes": "string",
|
||||
"bestFor": "string",
|
||||
"maxReuse": 0,
|
||||
"addToSecondary": true,
|
||||
"amountType": "Weight",
|
||||
"inventoryAmount": 0,
|
||||
"archived": true,
|
||||
"stockLevel": 0,
|
||||
"globalYeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"isGlobal": true
|
||||
},
|
||||
"sentAlerts": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:56:29.302Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alertText": "string",
|
||||
"trigger": "StepStart",
|
||||
"operator": "Equals",
|
||||
"temperature": 0,
|
||||
"gravity": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"betaUpdates": true,
|
||||
"bluetoothEnabled": true,
|
||||
"graphZoomLevel": 0,
|
||||
"temperature": 0,
|
||||
"targetTemperature": 0,
|
||||
"minTargetTemperature": 0,
|
||||
"maxTargetTemperature": 0,
|
||||
"totalRunTime": 0,
|
||||
"coolingEnabled": true,
|
||||
"coolingRunTime": 0,
|
||||
"coolingStarts": 0,
|
||||
"heatingEnabled": true,
|
||||
"heatingRunTime": 0,
|
||||
"heatingStarts": 0,
|
||||
"heatingUtilisation": 0,
|
||||
"highTempAlarm": 0,
|
||||
"lowTempAlarm": 0,
|
||||
"ntcBeta": 0,
|
||||
"ntcRefResistance": 0,
|
||||
"ntcRefTemperature": 0,
|
||||
"pidCycleTime": 0,
|
||||
"pidEnabled": true,
|
||||
"pidProportional": 0,
|
||||
"pidIntegral": 0,
|
||||
"pidDerivative": 0,
|
||||
"sensorDifferential": 0,
|
||||
"sensorTimeout": 0,
|
||||
"showGraph": true,
|
||||
"soundsEnabled": true,
|
||||
"tempUnit": "string",
|
||||
"useInternalSensor": true,
|
||||
"controlDeviceType": "string",
|
||||
"controlDeviceMacAddress": "string",
|
||||
"controlDeviceTemperature": 0,
|
||||
"customerUse": "string",
|
||||
"telemetryFrequency": 14440,
|
||||
"compressorDelay": 10,
|
||||
"modeSwitchDelay": 30,
|
||||
"coolingHysteresis": 10,
|
||||
"heatingHysteresis": 10,
|
||||
"telemetry": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"rowKey": "string",
|
||||
"createdOn": "2025-10-09T11:56:29.302Z",
|
||||
"macAddress": "string",
|
||||
"rssi": 0,
|
||||
"controlDeviceType": "string",
|
||||
"controlDeviceMacAddress": "string",
|
||||
"controlDeviceTemperature": 0,
|
||||
"temperature": 0,
|
||||
"targetTemperature": 0,
|
||||
"minTargetTemperature": 0,
|
||||
"maxTargetTemperature": 0,
|
||||
"totalRunTime": 0,
|
||||
"compressorRunTime": 0,
|
||||
"compressorStarts": 0,
|
||||
"heatingRunTime": 0,
|
||||
"heatingStarts": 0,
|
||||
"auxillaryRunTime": 0,
|
||||
"auxillaryStarts": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profileStepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profileSessionStartDate": "2025-10-09T11:56:29.302Z",
|
||||
"profileSessionTime": 0,
|
||||
"profileStepProgress": 0
|
||||
}
|
||||
],
|
||||
"compressorRunTime": 0,
|
||||
"compressorStarts": 0,
|
||||
"auxillaryRunTime": 0,
|
||||
"auxillaryStarts": 0,
|
||||
"fanEnabled": true,
|
||||
"lightEnabled": "AlwaysOn"
|
||||
}
|
28
tests/json/get_fermentation_chamber_telemetry_response.json
Normal file
28
tests/json/get_fermentation_chamber_telemetry_response.json
Normal file
@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"rowKey": "string",
|
||||
"createdOn": "2025-10-09T09:29:04.638Z",
|
||||
"macAddress": "string",
|
||||
"rssi": 0,
|
||||
"controlDeviceType": "string",
|
||||
"controlDeviceMacAddress": "string",
|
||||
"controlDeviceTemperature": 0,
|
||||
"temperature": 0,
|
||||
"targetTemperature": 0,
|
||||
"minTargetTemperature": 0,
|
||||
"maxTargetTemperature": 0,
|
||||
"totalRunTime": 0,
|
||||
"compressorRunTime": 0,
|
||||
"compressorStarts": 0,
|
||||
"heatingRunTime": 0,
|
||||
"heatingStarts": 0,
|
||||
"auxillaryRunTime": 0,
|
||||
"auxillaryStarts": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profileStepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profileSessionStartDate": "2025-10-09T09:29:04.638Z",
|
||||
"profileSessionTime": 0,
|
||||
"profileStepProgress": 0
|
||||
}
|
||||
]
|
386
tests/json/get_fermentation_chambers_response.json
Normal file
386
tests/json/get_fermentation_chambers_response.json
Normal file
@ -0,0 +1,386 @@
|
||||
[
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"serialNumber": "string",
|
||||
"macAddress": "string",
|
||||
"deviceType": "FermentationChamber",
|
||||
"active": true,
|
||||
"disabled": true,
|
||||
"username": "string",
|
||||
"connectionState": "string",
|
||||
"status": "string",
|
||||
"error": "string",
|
||||
"lastActivityTime": "2025-10-09T11:13:05.885Z",
|
||||
"rssi": 0,
|
||||
"firmwareVersion": "string",
|
||||
"isLatestFirmware": true,
|
||||
"activeProfileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"activeProfileStepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"activeProfileSession": {
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profile": {
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"public": true,
|
||||
"profileName": "string",
|
||||
"rating": 0,
|
||||
"ratingCount": 0,
|
||||
"ratingScore": 0,
|
||||
"copyCount": 0,
|
||||
"viewCount": 0,
|
||||
"profileTypeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alerts": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alertText": "string",
|
||||
"trigger": "StepStart",
|
||||
"operator": "Equals",
|
||||
"temperature": 0,
|
||||
"gravity": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"order": 0,
|
||||
"controlType": "Target",
|
||||
"endType": "Duration",
|
||||
"durationType": "Start",
|
||||
"operator": "Equals",
|
||||
"length": 0,
|
||||
"temperature": 0,
|
||||
"minTemperature": 0,
|
||||
"maxTemperature": 0,
|
||||
"gravity": 0,
|
||||
"pumpEnabled": true,
|
||||
"pumpUtilisation": 0,
|
||||
"heatingUtilisation": 0,
|
||||
"pidEnabled": true,
|
||||
"sensorDifferential": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alerts": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alertText": "string",
|
||||
"trigger": "StepStart",
|
||||
"operator": "Equals",
|
||||
"temperature": 0,
|
||||
"gravity": 0,
|
||||
"length": 0,
|
||||
"profileStepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"profileSessions": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profile": "string",
|
||||
"brewZillaId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"fermentationChamberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"hydrometerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"stillId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"temperatureControllerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"startDate": "2025-10-09T11:13:05.885Z",
|
||||
"endDate": "2025-10-09T11:13:05.885Z",
|
||||
"originalGravity": 0,
|
||||
"finalGravity": 0,
|
||||
"yeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"yeast": {
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"productId": "string",
|
||||
"laboratory": "string",
|
||||
"supplier": "string",
|
||||
"keglandProductCode": "string",
|
||||
"type": "string",
|
||||
"form": "string",
|
||||
"minTemperature": 0,
|
||||
"maxTemperature": 0,
|
||||
"flocculation": "string",
|
||||
"attenuation": 0,
|
||||
"notes": "string",
|
||||
"bestFor": "string",
|
||||
"maxReuse": 0,
|
||||
"addToSecondary": true,
|
||||
"amountType": "Weight",
|
||||
"inventoryAmount": 0,
|
||||
"archived": true,
|
||||
"stockLevel": 0,
|
||||
"globalYeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"isGlobal": true
|
||||
},
|
||||
"sentAlerts": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alertText": "string",
|
||||
"trigger": "StepStart",
|
||||
"operator": "Equals",
|
||||
"temperature": 0,
|
||||
"gravity": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"brewZillaId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"fermentationChamberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"hydrometerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"stillId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"temperatureControllerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"startDate": "2025-10-09T11:13:05.885Z",
|
||||
"endDate": "2025-10-09T11:13:05.885Z",
|
||||
"originalGravity": 0,
|
||||
"finalGravity": 0,
|
||||
"yeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"yeast": {
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"productId": "string",
|
||||
"laboratory": "string",
|
||||
"supplier": "string",
|
||||
"keglandProductCode": "string",
|
||||
"type": "string",
|
||||
"form": "string",
|
||||
"minTemperature": 0,
|
||||
"maxTemperature": 0,
|
||||
"flocculation": "string",
|
||||
"attenuation": 0,
|
||||
"notes": "string",
|
||||
"bestFor": "string",
|
||||
"maxReuse": 0,
|
||||
"addToSecondary": true,
|
||||
"amountType": "Weight",
|
||||
"inventoryAmount": 0,
|
||||
"archived": true,
|
||||
"stockLevel": 0,
|
||||
"globalYeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"isGlobal": true
|
||||
},
|
||||
"sentAlerts": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alertText": "string",
|
||||
"trigger": "StepStart",
|
||||
"operator": "Equals",
|
||||
"temperature": 0,
|
||||
"gravity": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
],
|
||||
"estimatedEndDate": "2025-10-09T11:13:05.885Z",
|
||||
"profileLength": 0,
|
||||
"currentProfileTime": 0,
|
||||
"remainingProfileTime": 0
|
||||
},
|
||||
"profileSessions": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"description": "string",
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profile": "string",
|
||||
"brewZillaId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"fermentationChamberId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"hydrometerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"stillId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"temperatureControllerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"startDate": "2025-10-09T11:13:05.885Z",
|
||||
"endDate": "2025-10-09T11:13:05.885Z",
|
||||
"originalGravity": 0,
|
||||
"finalGravity": 0,
|
||||
"yeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"yeast": {
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"name": "string",
|
||||
"productId": "string",
|
||||
"laboratory": "string",
|
||||
"supplier": "string",
|
||||
"keglandProductCode": "string",
|
||||
"type": "string",
|
||||
"form": "string",
|
||||
"minTemperature": 0,
|
||||
"maxTemperature": 0,
|
||||
"flocculation": "string",
|
||||
"attenuation": 0,
|
||||
"notes": "string",
|
||||
"bestFor": "string",
|
||||
"maxReuse": 0,
|
||||
"addToSecondary": true,
|
||||
"amountType": "Weight",
|
||||
"inventoryAmount": 0,
|
||||
"archived": true,
|
||||
"stockLevel": 0,
|
||||
"globalYeastId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"isGlobal": true
|
||||
},
|
||||
"sentAlerts": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"deleted": true,
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"createdBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"modifiedOn": "2025-10-09T11:13:05.885Z",
|
||||
"modifiedBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"alertText": "string",
|
||||
"trigger": "StepStart",
|
||||
"operator": "Equals",
|
||||
"temperature": 0,
|
||||
"gravity": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"betaUpdates": true,
|
||||
"bluetoothEnabled": true,
|
||||
"graphZoomLevel": 0,
|
||||
"temperature": 0,
|
||||
"targetTemperature": 0,
|
||||
"minTargetTemperature": 0,
|
||||
"maxTargetTemperature": 0,
|
||||
"totalRunTime": 0,
|
||||
"coolingEnabled": true,
|
||||
"coolingRunTime": 0,
|
||||
"coolingStarts": 0,
|
||||
"heatingEnabled": true,
|
||||
"heatingRunTime": 0,
|
||||
"heatingStarts": 0,
|
||||
"heatingUtilisation": 0,
|
||||
"highTempAlarm": 0,
|
||||
"lowTempAlarm": 0,
|
||||
"ntcBeta": 0,
|
||||
"ntcRefResistance": 0,
|
||||
"ntcRefTemperature": 0,
|
||||
"pidCycleTime": 0,
|
||||
"pidEnabled": true,
|
||||
"pidProportional": 0,
|
||||
"pidIntegral": 0,
|
||||
"pidDerivative": 0,
|
||||
"sensorDifferential": 0,
|
||||
"sensorTimeout": 0,
|
||||
"showGraph": true,
|
||||
"soundsEnabled": true,
|
||||
"tempUnit": "string",
|
||||
"useInternalSensor": true,
|
||||
"controlDeviceType": "string",
|
||||
"controlDeviceMacAddress": "string",
|
||||
"controlDeviceTemperature": 0,
|
||||
"customerUse": "string",
|
||||
"telemetryFrequency": 14440,
|
||||
"compressorDelay": 10,
|
||||
"modeSwitchDelay": 30,
|
||||
"coolingHysteresis": 10,
|
||||
"heatingHysteresis": 10,
|
||||
"telemetry": [
|
||||
{
|
||||
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"rowKey": "string",
|
||||
"createdOn": "2025-10-09T11:13:05.885Z",
|
||||
"macAddress": "string",
|
||||
"rssi": 0,
|
||||
"controlDeviceType": "string",
|
||||
"controlDeviceMacAddress": "string",
|
||||
"controlDeviceTemperature": 0,
|
||||
"temperature": 0,
|
||||
"targetTemperature": 0,
|
||||
"minTargetTemperature": 0,
|
||||
"maxTargetTemperature": 0,
|
||||
"totalRunTime": 0,
|
||||
"compressorRunTime": 0,
|
||||
"compressorStarts": 0,
|
||||
"heatingRunTime": 0,
|
||||
"heatingStarts": 0,
|
||||
"auxillaryRunTime": 0,
|
||||
"auxillaryStarts": 0,
|
||||
"profileId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profileStepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"profileSessionStartDate": "2025-10-09T11:13:05.885Z",
|
||||
"profileSessionTime": 0,
|
||||
"profileStepProgress": 0
|
||||
}
|
||||
],
|
||||
"compressorRunTime": 0,
|
||||
"compressorStarts": 0,
|
||||
"auxillaryRunTime": 0,
|
||||
"auxillaryStarts": 0,
|
||||
"fanEnabled": true,
|
||||
"lightEnabled": "AlwaysOn"
|
||||
}
|
||||
]
|
35
tests/json/get_hydrometer_response.json
Normal file
35
tests/json/get_hydrometer_response.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"telemetry": [
|
||||
{
|
||||
"temperature": 17.5625,
|
||||
"gravity": 1325.07,
|
||||
"gravityVelocity": 0,
|
||||
"battery": 0,
|
||||
"version": "20250319_055542_b3788ba",
|
||||
"id": "68423584-1a35-4341-b67c-78456f4ae9f3",
|
||||
"rowKey": "2516424593326678442",
|
||||
"createdOn": "2025-10-07T12:51:07.3321557+00:00",
|
||||
"macAddress": "ac-15-18-df-84-94",
|
||||
"rssi": -56
|
||||
}
|
||||
],
|
||||
"temperature": 17.5625,
|
||||
"gravity": 1325.07,
|
||||
"gravityVelocity": 0,
|
||||
"battery": 0,
|
||||
"name": "Hegnsgården Yellow",
|
||||
"macAddress": "ac-15-18-df-84-94",
|
||||
"deviceType": "Hydrometer",
|
||||
"active": false,
|
||||
"disabled": false,
|
||||
"lastActivityTime": "2025-10-07T12:51:07.3321557+00:00",
|
||||
"rssi": -56,
|
||||
"firmwareVersion": "20250319_055542_b3788ba",
|
||||
"isLatestFirmware": true,
|
||||
"modifiedOn": "2025-10-07T12:51:07.3603727+00:00",
|
||||
"modifiedBy": "00000000-0000-0000-0000-000000000000",
|
||||
"id": "2aa3b02c-78de-4715-8f5b-61bf7c3d1b62",
|
||||
"deleted": false,
|
||||
"createdOn": "2025-10-07T09:44:01.1515163+00:00",
|
||||
"createdBy": "475ebc33-8e90-4be5-2424-08ddfbe49482"
|
||||
}
|
14
tests/json/get_hydrometer_telemetry_response.json
Normal file
14
tests/json/get_hydrometer_telemetry_response.json
Normal file
@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"temperature": 17.5625,
|
||||
"gravity": 1325.07,
|
||||
"gravityVelocity": 0,
|
||||
"battery": 0,
|
||||
"version": "20250319_055542_b3788ba",
|
||||
"id": "50d5885c-b637-4343-bb47-cc76e9f3c0c1",
|
||||
"rowKey": "2516424593326678442",
|
||||
"createdOn": "2025-10-07T12:51:07.3321557+00:00",
|
||||
"macAddress": "ac-15-18-df-84-94",
|
||||
"rssi": -56
|
||||
}
|
||||
]
|
37
tests/json/get_hydrometers_response.json
Normal file
37
tests/json/get_hydrometers_response.json
Normal file
@ -0,0 +1,37 @@
|
||||
[
|
||||
{
|
||||
"telemetry": [
|
||||
{
|
||||
"temperature": 17.5625,
|
||||
"gravity": 1325.07,
|
||||
"gravityVelocity": 0,
|
||||
"battery": 0,
|
||||
"version": "20250319_055542_b3788ba",
|
||||
"id": "55462886-4400-4608-b283-3ce4426dafd3",
|
||||
"rowKey": "2516424593326678442",
|
||||
"createdOn": "2025-10-07T12:51:07.3321557+00:00",
|
||||
"macAddress": "ac-15-18-df-84-94",
|
||||
"rssi": -56
|
||||
}
|
||||
],
|
||||
"temperature": 17.5625,
|
||||
"gravity": 1325.07,
|
||||
"gravityVelocity": 0,
|
||||
"battery": 0,
|
||||
"name": "Hegnsgården Yellow",
|
||||
"macAddress": "ac-15-18-df-84-94",
|
||||
"deviceType": "Hydrometer",
|
||||
"active": false,
|
||||
"disabled": false,
|
||||
"lastActivityTime": "2025-10-07T12:51:07.3321557+00:00",
|
||||
"rssi": -56,
|
||||
"firmwareVersion": "20250319_055542_b3788ba",
|
||||
"isLatestFirmware": false,
|
||||
"modifiedOn": "2025-10-07T12:51:07.3603727+00:00",
|
||||
"modifiedBy": "00000000-0000-0000-0000-000000000000",
|
||||
"id": "2aa3b02c-78de-4715-8f5b-61bf7c3d1b62",
|
||||
"deleted": false,
|
||||
"createdOn": "2025-10-07T09:44:01.1515163+00:00",
|
||||
"createdBy": "475ebc33-8e90-4be5-2424-08ddfbe49482"
|
||||
}
|
||||
]
|
6
tests/json/token_response.json
Normal file
6
tests/json/token_response.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkYzM0E0QzYzNkZFOUE3RjBFNzVGQkRBQTQ1REIwOTdBQjUxRUVGMTlSUzI1NiIsIng1dCI6Ijh6cE1ZMl9wcF9Eblg3MnFSZHNKZXJVZTd4ayIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwczovL2lkLnJhcHQuaW8iLCJuYmYiOjE3NTk5MjcyMjQsImlhdCI6MTc1OTkyNzIyNCwiZXhwIjoxNzU5OTMwODI0LCJhdWQiOiJyYXB0LWFwaSIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJyYXB0LWFwaSIsInJhcHQtYXBpLnB1YmxpYyJdLCJhbXIiOlsiY3VzdG9tIl0sImNsaWVudF9pZCI6InJhcHQtdXNlciIsInN1YiI6IjQ3NWViYzMzLThlOTAtNGJlNS0yNDI0LTA4ZGRmYmU0OTQ4MiIsImF1dGhfdGltZSI6MTc1OTkyNzIyNCwiaWRwIjoibG9jYWwiLCJmaXJzdG5hbWUiOiJKZXNwZXIiLCJsYXN0bmFtZSI6IkZ1c3NpbmcgTVx1MDBGOHJrIiwiY291bnRyeSI6IkRlbm1hcmsiLCJlbWFpbCI6ImpmbUBtb2Vya3MuZGsiLCJwcm9maWxlbmFtZSI6ImpmbSIsImVtYWlsY29uZmlybWVkIjoiZmFsc2UiLCJqdGkiOiI3RDM3QjcxMDNBMTc4QkQ0RkMwM0ZCRDNGNTBFMDY2MiJ9.HJtC6Cfps5rqsukMYFRi7S4awpz0KqJjSX1EdL1K6NMpxVEmqmiiA3LwXrsjCbu2FoLjswxyM7rCFqVBZnnQ71DcQJ9gJCe1Ddonj-l5SfA90KHjlvLPQOhvCLrYHc9ulDEi64Dz-WfH8V7WCy1fgl3lY8xmRstS8rjO-5jVwQFFlZvyOHoke09iMsRQmCyuiGYbqmn1i31VSK6L3fMuRfrc1o165T1Xts66vAupJ6nqEhHnAusvCM2gagEvAiXx3hH3xxid4K6zCQLvbg0jxKUHnKI9q2xVj30bW0C2_lKSDKpOOTLjWpjrwjVag5DYxYFggNVgTvF90f_hYddxFX8s9_dgY6EbTunSA1ki86r82Rw7Ebvfwy1phjNHllgu85Y73tIYADcQYxzjglNKPX1Dzed8liUOAXTHgL2Z9-4_q8bwmgNrGy9eH4K_PjyqEuuhDvy2GQxjhOojOXJOU-aSaE4KwaHTod1CZB-kaikMAKjsHDTi3cRspFU4sli665zgPZ2UKYsjTWSlqoKpA7wO3RrMKQiCrNEqyE0ldOi1ctCS-dBEBkuhc-THTwOqMDQTQ6Iqqk1dK72Nm8uB5TwPur-TP4YYxi1hY6w0jSOs0JzUVpaMGgAXg3CM_zhWgTE9S44R3gjxX6EsXayI5f5WtstxpHMbB3rO_DrZPjA",
|
||||
"expires_in": 3600,
|
||||
"token_type": "Bearer",
|
||||
"scope": "openid profile rapt-api rapt-api.public"
|
||||
}
|
83
tests/test_fermentation_chamber.py
Normal file
83
tests/test_fermentation_chamber.py
Normal file
@ -0,0 +1,83 @@
|
||||
import pytest
|
||||
import json
|
||||
import responses
|
||||
from rapt.client import Client
|
||||
from rapt.fermentation_chamber import FermentationChamber
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
yield Client("test", "test")
|
||||
|
||||
@responses.activate
|
||||
def test_get_fermentation_chambers(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.get("https://api.rapt.io/api/fermentationchambers/getfermentationchambers", json=json_reader("./tests/json/get_fermentation_chambers_response.json"))
|
||||
fermentation_chamber = FermentationChamber(client)
|
||||
ferms = fermentation_chamber.get_fermentation_chambers()
|
||||
|
||||
assert ferms is not None
|
||||
assert len(ferms) == 1
|
||||
|
||||
@responses.activate
|
||||
def test_get_fermentation_chambers_500(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.add(
|
||||
responses.GET,
|
||||
"https://api.rapt.io/api/fermentationchambers/getfermentationchambers",
|
||||
status=500,
|
||||
)
|
||||
fermentation_chamber = FermentationChamber(client)
|
||||
ferms = fermentation_chamber.get_fermentation_chambers()
|
||||
|
||||
assert ferms is None
|
||||
|
||||
@responses.activate
|
||||
def test_get_fermentation_chamber(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.get("https://api.rapt.io/api/fermentationchambers/getfermentationchamber", json=json_reader("./tests/json/get_fermentation_chamber_response.json"))
|
||||
fermentation_chamber = FermentationChamber(client)
|
||||
ferms = fermentation_chamber.get_fermentation_chamber("")
|
||||
|
||||
assert ferms is not None
|
||||
|
||||
@responses.activate
|
||||
def test_get_fermentation_chamber_500(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.add(
|
||||
responses.GET,
|
||||
"https://api.rapt.io/api/fermentationchambers/getfermentationchamber",
|
||||
status=500,
|
||||
)
|
||||
fermentation_chamber = FermentationChamber(client)
|
||||
ferms = fermentation_chamber.get_fermentation_chamber("")
|
||||
|
||||
assert ferms is None
|
||||
|
||||
@responses.activate
|
||||
def test_get_telemetry(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.get("https://api.rapt.io/api/fermentationchambers/gettelemetry", json=json_reader("./tests/json/get_fermentation_chamber_telemetry_response.json"))
|
||||
fermentation_chamber = FermentationChamber(client)
|
||||
telemetry = fermentation_chamber.get_telemetry("", "", "", "")
|
||||
|
||||
assert telemetry is not None
|
||||
assert len(telemetry) == 1
|
||||
|
||||
@responses.activate
|
||||
def test_get_telemetry_500(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.add(
|
||||
responses.GET,
|
||||
"https://api.rapt.io/api/fermentationchambers/gettelemetry",
|
||||
status=500,
|
||||
)
|
||||
fermentation_chamber = FermentationChamber(client)
|
||||
telemetry = fermentation_chamber.get_telemetry("", "", "", "")
|
||||
|
||||
assert telemetry is None
|
||||
|
||||
|
||||
def json_reader(path):
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
83
tests/test_hydrometer.py
Normal file
83
tests/test_hydrometer.py
Normal file
@ -0,0 +1,83 @@
|
||||
import pytest
|
||||
import json
|
||||
import responses
|
||||
from rapt.client import Client
|
||||
from rapt.hydrometer import Hydrometer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
yield Client("test", "test")
|
||||
|
||||
@responses.activate
|
||||
def test_get_hydrometers(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.get("https://api.rapt.io/api/hydrometers/gethydrometers", json=json_reader("./tests/json/get_hydrometers_response.json"))
|
||||
hydrometer = Hydrometer(client)
|
||||
hydros = hydrometer.get_hydrometers()
|
||||
|
||||
assert hydros is not None
|
||||
assert len(hydros) == 1
|
||||
|
||||
@responses.activate
|
||||
def test_get_hydrometers_500(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.add(
|
||||
responses.GET,
|
||||
"https://api.rapt.io/api/hydrometers/gethydrometers",
|
||||
status=500,
|
||||
)
|
||||
hydrometer = Hydrometer(client)
|
||||
hydros = hydrometer.get_hydrometers()
|
||||
|
||||
assert hydros is None
|
||||
|
||||
@responses.activate
|
||||
def test_get_hydrometer(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.get("https://api.rapt.io/api/hydrometers/gethydrometer", json=json_reader("./tests/json/get_hydrometer_response.json"))
|
||||
hydrometer = Hydrometer(client)
|
||||
hydros = hydrometer.get_hydrometer("")
|
||||
|
||||
assert hydros is not None
|
||||
|
||||
@responses.activate
|
||||
def test_get_hydrometer_500(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.add(
|
||||
responses.GET,
|
||||
"https://api.rapt.io/api/hydrometers/gethydrometer",
|
||||
status=500,
|
||||
)
|
||||
hydrometer = Hydrometer(client)
|
||||
hydros = hydrometer.get_hydrometer("")
|
||||
|
||||
assert hydros is None
|
||||
|
||||
|
||||
@responses.activate
|
||||
def test_get_telemetry(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.get("https://api.rapt.io/api/hydrometers/gettelemetry", json=json_reader("./tests/json/get_hydrometer_telemetry_response.json"))
|
||||
hydrometer = Hydrometer(client)
|
||||
telemetry = hydrometer.get_telemetry("", "", "", "")
|
||||
|
||||
assert telemetry is not None
|
||||
|
||||
@responses.activate
|
||||
def test_get_telemetry_500(client):
|
||||
responses.post("https://id.rapt.io/connect/token", json=json_reader("./tests/json/token_response.json"))
|
||||
responses.add(
|
||||
responses.GET,
|
||||
"https://api.rapt.io/api/hydrometers/gettelemetry",
|
||||
status=500,
|
||||
)
|
||||
hydrometer = Hydrometer(client)
|
||||
telemetry = hydrometer.get_telemetry("", "", "", "")
|
||||
|
||||
assert telemetry is None
|
||||
|
||||
|
||||
def json_reader(path):
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
Loading…
x
Reference in New Issue
Block a user