Skip to content

Commit

Permalink
Custom stream WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
MvdDonk committed Feb 12, 2025
1 parent 608272b commit 1796594
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 9 deletions.
14 changes: 13 additions & 1 deletion custom_components/brewfather/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
CONF_MULTI_BATCH,
CONF_ALL_BATCH_INFO_SENSOR,
VERSION_MAJOR,
VERSION_MINOR
VERSION_MINOR,
CONF_CUSTOM_STREAM_ENABLED,
CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_NAME,
CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_ATTRIBUTE,
CONF_CUSTOM_STREAM_LOGGING_ID,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -85,6 +89,14 @@ async def async_migrate_entry(hass, config_entry: config_entries.ConfigEntry):
new_data[CONF_ALL_BATCH_INFO_SENSOR] = False
pass

if config_entry.minor_version < 4:
new_data[CONF_CUSTOM_STREAM_ENABLED] = False
new_data[CONF_CUSTOM_STREAM_LOGGING_ID] = ""

new_data[CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_NAME] = ""
new_data[CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_ATTRIBUTE] = ""
pass

hass.config_entries.async_update_entry(config_entry, data=new_data, minor_version=VERSION_MINOR, version=VERSION_MAJOR)

_LOGGER.debug("Migration to configuration version %s.%s successful", config_entry.version, config_entry.minor_version)
Expand Down
42 changes: 37 additions & 5 deletions custom_components/brewfather/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
VERSION_MINOR,
CONF_MULTI_BATCH,
CONF_ALL_BATCH_INFO_SENSOR,
CONF_CUSTOM_STREAM_ENABLED,
CONF_CUSTOM_STREAM_LOGGING_ID,
CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_NAME,
CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_ATTRIBUTE,
)
from .connection import (
Connection,
Expand Down Expand Up @@ -42,6 +46,10 @@
vol.Required(CONF_RAMP_TEMP_CORRECTION): cv.boolean,
vol.Required(CONF_MULTI_BATCH): cv.boolean,
vol.Required(CONF_ALL_BATCH_INFO_SENSOR): cv.boolean,
vol.Required(CONF_CUSTOM_STREAM_ENABLED): cv.boolean,
vol.Optional(CONF_CUSTOM_STREAM_LOGGING_ID): cv.string,
vol.Optional(CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_NAME): cv.string,
vol.Optional(CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_ATTRIBUTE): cv.string,
}
)

Expand Down Expand Up @@ -132,6 +140,9 @@ async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> config_entries.FlowResult:
"""Manage the options."""

errors: dict[str, str] = {}

if user_input is not None:

new_config = ConfigFlow.get_config_entry(
Expand All @@ -143,16 +154,37 @@ async def async_step_init(
user_input.get(CONF_ALL_BATCH_INFO_SENSOR)
)

# update config entry
self.hass.config_entries.async_update_entry(
self.config_entry, data=new_config, options=self.config_entry.options
)
if user_input.get(CONF_CUSTOM_STREAM_ENABLED):
return self.async_show_form(
step_id="custom_stream",
data_schema=self.add_suggested_values_to_schema(
OPTIONS_SCHEMA, self.config_entry.data
),
errors=errors,
)

logging_id = user_input.get(CONF_CUSTOM_STREAM_LOGGING_ID)
if logging_id is None or logging_id == "":
#errors = {CONF_CUSTOM_STREAM_LOGGING_ID: "empty"}
errors["base"] = "dsadasdas"
errors[CONF_CUSTOM_STREAM_ENABLED] = "zzzzxcxzc"

entity_name = user_input.get(CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_NAME)
if entity_name is None or entity_name == "":
errors[CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_NAME] = "moet!"

if errors is None and errors.length == 0:
# update config entry
self.hass.config_entries.async_update_entry(
self.config_entry, data=new_config, options=self.config_entry.options
)

return self.async_create_entry(title="Brewfather", data=new_config)
return self.async_create_entry(title="Brewfather", data=new_config)

return self.async_show_form(
step_id="init",
data_schema=self.add_suggested_values_to_schema(
OPTIONS_SCHEMA, self.config_entry.data
),
errors=errors,
)
40 changes: 39 additions & 1 deletion custom_components/brewfather/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .models.batches_item import BatchesItemElement, batches_item_from_dict
from .models.batch_item import BatchItem, batch_item_from_dict
from .models.reading_item import Reading, readings_from_dict
from .models.custom_stream_data import custom_stream_data

from homeassistant.helpers.update_coordinator import UpdateFailed
from .const import (
Expand All @@ -15,7 +16,8 @@
BATCH_URI,
READINGS_URI,
DRY_RUN,
LAST_READING_URI
LAST_READING_URI,
LOG_CUSTOM_STREAM
)
from .testdata import (
TESTDATA_BATCHES,
Expand Down Expand Up @@ -81,6 +83,30 @@ async def get_last_reading(self, batchId: str) -> Reading:
reading = await self.get_api_response(url, Reading.from_dict, accept_404 = True)
return reading

async def post_custom_stream(self, logging_id: str, data:custom_stream_data) -> bool:
url = LOG_CUSTOM_STREAM.format(logging_id)
if DRY_RUN:
raise Exception("Not implemented")
else:
success = await self.post(url, self.to_dict(data))
return success

def to_dict(self, obj):
"""
Convert an object to a dictionary.
Handles objects with __dict__, lists, tuples, and other types.
"""
if isinstance(obj, dict):
return {k: self.to_dict(v) for k, v in obj.items()}
elif hasattr(obj, "__dict__"):
return {k: self.to_dict(v) for k, v in obj.__dict__.items()}
elif isinstance(obj, list):
return [self.to_dict(i) for i in obj]
elif isinstance(obj, tuple):
return tuple(self.to_dict(i) for i in obj)
else:
return obj

async def get_api_response(self, url: str, parseJson:Callable[[str], T], accept_404: bool = False) -> T:
_LOGGER.debug("Making api call to: %s", url)
async with aiohttp.ClientSession() as session:
Expand All @@ -105,6 +131,18 @@ async def get_api_response(self, url: str, parseJson:Callable[[str], T], accept_
f"Error communicating with API: {response.status}, URL: {url}"
)

async def post(self, url: str, data: dict) -> bool:
_LOGGER.debug("Making api call to: %s, with body: %s", url, data)
async with aiohttp.ClientSession() as session:
async with session.post(url, json=data, auth=self.auth) as response:
if response.status == 200:
return True
else:
_LOGGER.debug("Failed posting to api, got status: %s", response.status)
raise UpdateFailed(
f"Error communicating with API: {response.status}, URL: {url}"
)

class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect."""

Expand Down
7 changes: 6 additions & 1 deletion custom_components/brewfather/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
BATCH_URI = "https://api.brewfather.app/v2/batches/{}?include=recipe.fermentation,notes,measuredOg"
READINGS_URI = "https://api.brewfather.app/v2/batches/{}/readings"
LAST_READING_URI = "https://api.brewfather.app/v2/batches/{}/readings/last"
LOG_CUSTOM_STREAM = "http://log.brewfather.net/stream?id={}"

DRY_RUN = False
CONF_RAMP_TEMP_CORRECTION = "ramp_temp_correction"
CONF_MULTI_BATCH = "multi_batch"
CONF_ALL_BATCH_INFO_SENSOR = "all_batch_info_sensor"
CONF_CUSTOM_STREAM_ENABLED = "custom_stream_enabled"
CONF_CUSTOM_STREAM_LOGGING_ID = "custom_stream_logging_id"
CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_NAME = "custom_stream_temperature_entity_name"
CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_ATTRIBUTE = "custom_stream_temperature_entity_attribute"

VERSION_MAJOR = 1
VERSION_MINOR = 3
VERSION_MINOR = 4
44 changes: 43 additions & 1 deletion custom_components/brewfather/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Step,
Reading
)
from .models.custom_stream_data import custom_stream_data
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
Expand All @@ -20,7 +21,11 @@
MS_IN_DAY,
CONF_RAMP_TEMP_CORRECTION,
CONF_MULTI_BATCH,
CONF_ALL_BATCH_INFO_SENSOR
CONF_ALL_BATCH_INFO_SENSOR,
CONF_CUSTOM_STREAM_ENABLED,
CONF_CUSTOM_STREAM_LOGGING_ID,
CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_NAME,
CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_ATTRIBUTE
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -71,6 +76,12 @@ def __init__(self, hass: HomeAssistant, entry, update_interval: timedelta):
entry.data.get(CONF_USERNAME),
entry.data.get(CONF_PASSWORD)
)
self.custom_stream_enabled = entry.data.get(CONF_CUSTOM_STREAM_ENABLED, False)
if self.custom_stream_enabled:
self.custom_stream_logging_id = entry.data.get(CONF_CUSTOM_STREAM_LOGGING_ID, None)

self.custom_stream_temperature_entity_name = entry.data.get(CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_NAME, None)
self.custom_stream_temperature_entity_attribute = entry.data.get(CONF_CUSTOM_STREAM_TEMPERATURE_ENTITY_ATTRIBUTE, None)

super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)

Expand All @@ -87,6 +98,17 @@ async def update(self) -> BrewfatherCoordinatorData:
fermentingBatches:list[BatchInfo] = []
all_batches_data:list[BatchInfo] = []

#if custom stream enabled
if self.custom_stream_enabled:
stream_data = self.create_custom_stream_data()
if stream_data is not None:
_LOGGER.debug("No data was found to post to custom stream")
else:
_LOGGER.debug("Posting custom stream data")
success = await self.connection.post_custom_stream(self.custom_stream_logging_id, stream_data)
if not success:
_LOGGER.error("Failed to post custom stream data")

for batch in allBatches:
batchData = await self.connection.get_batch(batch.id)
last_reading = await self.connection.get_last_reading(batch.id)
Expand Down Expand Up @@ -272,3 +294,23 @@ def datetime_fromtimestamp_with_fermentingstart(

return datetime_value

def create_custom_stream_data(self) -> Optional[custom_stream_data]:
stream_data = custom_stream_data(name = "HomeAssistant")

stream_data.temp_unit = "C"
entity = self.hass.states.get(self.custom_stream_temperature_entity_name)
if entity is None:
return None

if self.custom_stream_temperature_entity_attribute is None:
stream_data.temp = entity.state
else:
stream_data.temp_unit = entity.attributes.get(self.custom_stream_temperature_entity_attribute)

if datetime.now().timestamp() % 2 == 0:
stream_data.temp = 14.2
else:
stream_data.temp = 16

#self.hass.states.get(temperature_sensor).attributes.get(temperature_sensor_attribute)
return stream_data
9 changes: 9 additions & 0 deletions custom_components/brewfather/models/custom_stream_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from typing import Optional

class custom_stream_data:
name: str
temp: Optional[float]
temp_unit: Optional[str]

def __init__(self, name: str) -> None:
self.name = name

0 comments on commit 1796594

Please sign in to comment.