Merge pull request #64 from avollkopf/master

Fix Hysteresis and Add Boil Alarms + Fix Timer
This commit is contained in:
Manuel83 2021-04-12 23:01:09 +02:00 committed by GitHub
commit 54ed882ad5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 18 deletions

View file

@ -31,7 +31,7 @@ class Props:
return key in self.__data__ return key in self.__data__
def get(self, key, d=None): def get(self, key, d=None):
if key in self.__data__: if key in self.__data__ and self.__data__[key] != "":
return self.__data__[key] return self.__data__[key]
else: else:
return d return d

View file

@ -13,27 +13,29 @@ class Timer(object):
self._callback = on_done self._callback = on_done
self._update = on_update self._update = on_update
self.start_time = None self.start_time = None
self.end_time = None
def done(self, task): def done(self, task):
if self._callback is not None: if self._callback is not None:
asyncio.create_task(self._callback(self)) asyncio.create_task(self._callback(self))
async def _job(self): async def _job(self):
self.start_time = time.time() self.start_time = int(time.time())
self.count = int(round(self._timemout, 0)) self.end_time = self.start_time + int(round(self._timemout, 0))
self.count = self.end_time - self.start_time
try: try:
while self.count > 0: while self.count > 0:
self.count -= 1 self.count = (self.end_time - int(time.time()))
if self._update is not None: if self._update is not None:
await self._update(self,self.count) await self._update(self,self.count)
await asyncio.sleep(1) await asyncio.sleep(1)
except asyncio.CancelledError: except asyncio.CancelledError:
end = time.time() end = int(time.time())
duration = end - self.start_time duration = end - self.start_time
self._timemout = self._timemout - duration self._timemout = self._timemout - duration
async def add(self, seconds): async def add(self, seconds):
self.count = self.count + seconds self.end_time = self.end_time + seconds
def start(self): def start(self):
self._task = asyncio.create_task(self._job()) self._task = asyncio.create_task(self._job())
@ -66,4 +68,4 @@ class Timer(object):
seconds = time % 60 seconds = time % 60
minutes = math.floor(time / 60) % 60 minutes = math.floor(time / 60) % 60
hours = math.floor(time / 3600) hours = math.floor(time / 3600)
return pattern.format(hours, minutes, seconds) return pattern.format(hours, minutes, seconds)

View file

@ -4,7 +4,7 @@ import logging
from cbpi.api import * from cbpi.api import *
@parameters([Property.Number(label="OffsetOn", configurable=True, description="Offset below target temp when heater should switched on"), @parameters([Property.Number(label="OffsetOn", configurable=True, description="Offset below target temp when heater should switched on"),
Property.Number(label="OffsetOff", configurable=True, description="Offset above target temp when heater should switched off")]) Property.Number(label="OffsetOff", configurable=True, description="Offset below target temp when heater should switched off")])
class Hysteresis(CBPiKettleLogic): class Hysteresis(CBPiKettleLogic):
async def run(self): async def run(self):
@ -24,7 +24,7 @@ class Hysteresis(CBPiKettleLogic):
target_temp = self.get_kettle_target_temp(self.id) target_temp = self.get_kettle_target_temp(self.id)
if sensor_value < target_temp - self.offset_on: if sensor_value < target_temp - self.offset_on:
await self.actor_on(self.heater) await self.actor_on(self.heater)
elif sensor_value >= target_temp + self.offset_off: elif sensor_value >= target_temp - self.offset_off:
await self.actor_off(self.heater) await self.actor_off(self.heater)
await asyncio.sleep(1) await asyncio.sleep(1)

View file

@ -3,6 +3,10 @@ import asyncio
from cbpi.api import parameters, Property, action from cbpi.api import parameters, Property, action
from cbpi.api.step import StepResult, CBPiStep from cbpi.api.step import StepResult, CBPiStep
from cbpi.api.timer import Timer from cbpi.api.timer import Timer
from datetime import datetime
import time
from voluptuous.schema_builder import message
from cbpi.api.dataclasses import NotificationAction, NotificationType
@parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True), @parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True),
@ -115,20 +119,51 @@ class ActorStep(CBPiStep):
return StepResult.DONE return StepResult.DONE
@parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True), @parameters([Property.Number(label="Timer", description="Time in Minutes", configurable=True),
Property.Number(label="Temp", description="Boil temperature", configurable=True), Property.Number(label="Temp", description="Boil temperature", configurable=True),
Property.Sensor(label="Sensor"), Property.Sensor(label="Sensor"),
Property.Kettle(label="Kettle")]) Property.Kettle(label="Kettle"),
Property.Select("First_Wort", options=["Yes","No"], description="First Wort Hop alert if set to Yes"),
Property.Number("Hop_1", configurable = True, description="First Hop alert (minutes before finish)"),
Property.Number("Hop_2", configurable=True, description="Second Hop alert (minutes before finish)"),
Property.Number("Hop_3", configurable=True, description="Third Hop alert (minutes before finish)"),
Property.Number("Hop_4", configurable=True, description="Fourth Hop alert (minutes before finish)"),
Property.Number("Hop_5", configurable=True, description="Fifth Hop alert (minutes before finish)"),
Property.Number("Hop_6", configurable=True, description="Sixth Hop alert (minutes before finish)")])
class BoilStep(CBPiStep): class BoilStep(CBPiStep):
@action("Start Timer", [])
async def start_timer(self):
if self.timer.is_running == None:
self.cbpi.notify(self.name, 'Timer started', NotificationType.INFO)
self.timer.start()
else:
self.cbpi.notify(self.name, 'Timer is already running', NotificationType.WARNING)
@action("Add 5 Minutes to Timer", [])
async def add_timer(self):
if self.timer.is_running != None:
self.cbpi.notify(self.name, '5 Minutes added', NotificationType.INFO)
await self.timer.add(300)
else:
self.cbpi.notify(self.name, 'Timer must be running to add time', NotificationType.WARNING)
async def on_timer_done(self, timer): async def on_timer_done(self, timer):
self.summary = "" self.summary = ""
await self.next() await self.next()
async def on_timer_update(self, timer, seconds): async def on_timer_update(self, timer, seconds):
self.summary = Timer.format_time(seconds) self.summary = Timer.format_time(seconds)
self.remaining_seconds = seconds
await self.push_update() await self.push_update()
async def check_hop_timer(self, number, value):
if value is not None and value != '' and self.hops_added[number-1] is not True:
if self.remaining_seconds != None and self.remaining_seconds <= (int(value) * 60 + 1):
self.hops_added[number-1]= True
self.cbpi.notify('Hop Alert', "Please add Hop %s" % number, NotificationType.INFO)
async def on_start(self): async def on_start(self):
if self.timer is None: if self.timer is None:
self.timer = Timer(int(self.props.Timer) * 60, on_update=self.on_timer_update, on_done=self.on_timer_done) self.timer = Timer(int(self.props.Timer) * 60, on_update=self.on_timer_update, on_done=self.on_timer_done)
@ -136,6 +171,10 @@ class BoilStep(CBPiStep):
await self.cbpi.kettle.set_target_temp(self.props.Kettle, int(self.props.Temp)) await self.cbpi.kettle.set_target_temp(self.props.Kettle, int(self.props.Temp))
self.summary = "Waiting for Target Temp" self.summary = "Waiting for Target Temp"
await self.push_update() await self.push_update()
self.first_wort_hop_flag = False
self.first_wort_hop=self.props.First_Wort
self.hops_added=["","","","","",""]
self.remaining_seconds = None
async def on_stop(self): async def on_stop(self):
await self.timer.stop() await self.timer.stop()
@ -145,18 +184,28 @@ class BoilStep(CBPiStep):
async def reset(self): async def reset(self):
self.timer = Timer(int(self.props.Timer) * 60, on_update=self.on_timer_update, on_done=self.on_timer_done) self.timer = Timer(int(self.props.Timer) * 60, on_update=self.on_timer_update, on_done=self.on_timer_done)
@action("Start Timer", [])
async def star_timer(self):
self.timer.start()
async def run(self): async def run(self):
if self.first_wort_hop_flag == False and self.first_wort_hop == "Yes":
self.first_wort_hop_flag = True
self.cbpi.notify('First Wort Hop Addition!', 'Please add hops for first wort', NotificationType.INFO)
while self.running == True: while self.running == True:
await asyncio.sleep(1) await asyncio.sleep(1)
sensor_value = self.get_sensor_value(self.props.Sensor) sensor_value = self.get_sensor_value(self.props.Sensor)
if sensor_value.get("value") >= int(self.props.Temp) and self.timer.is_running is not True: if sensor_value.get("value") >= int(self.props.Temp) and self.timer.is_running is not True:
self.timer.start() self.timer.start()
estimated_completion_time = datetime.fromtimestamp(time.time()+ (int(self.props.Timer))*60)
self.cbpi.notify(self.name, 'Timer started. Estimated completion: {}'.format(estimated_completion_time.strftime("%H:%M")), NotificationType.INFO)
self.timer.is_running = True self.timer.is_running = True
else:
await self.check_hop_timer(1, self.props.Hop_1)
await self.check_hop_timer(2, self.props.Hop_2)
await self.check_hop_timer(3, self.props.Hop_3)
await self.check_hop_timer(4, self.props.Hop_4)
await self.check_hop_timer(5, self.props.Hop_5)
await self.check_hop_timer(6, self.props.Hop_6)
return StepResult.DONE return StepResult.DONE

View file

@ -51,7 +51,9 @@ class ReadThread (threading.Thread):
time.sleep(1) time.sleep(1)
@parameters([Property.Select(label="Sensor", options=getSensors()), Property.Select(label="Interval", options=[1,5,10,30,60], description="Interval in Seconds")]) @parameters([Property.Select(label="Sensor", options=getSensors()),
Property.Number(label="offset",configurable = True, default_value = 0, description="Offset for the PT Sensor (Default is 0)"),
Property.Select(label="Interval", options=[1,5,10,30,60], description="Interval in Seconds")])
class OneWire(CBPiSensor): class OneWire(CBPiSensor):
def __init__(self, cbpi, id, props): def __init__(self, cbpi, id, props):
@ -62,6 +64,8 @@ class OneWire(CBPiSensor):
await super().start() await super().start()
self.name = self.props.get("Sensor") self.name = self.props.get("Sensor")
self.interval = self.props.get("Interval", 60) self.interval = self.props.get("Interval", 60)
self.offset = float(self.props.get("offset",0))
self.t = ReadThread(self.name) self.t = ReadThread(self.name)
self.t.daemon = True self.t.daemon = True
def shudown(): def shudown():
@ -78,7 +82,11 @@ class OneWire(CBPiSensor):
async def run(self): async def run(self):
while self.running == True: while self.running == True:
self.value = self.t.value self.TEMP_UNIT=self.get_config_value("TEMP_UNIT", "C")
if self.TEMP_UNIT == "C": # Report temp in C if nothing else is selected in settings
self.value = round((self.t.value + self.offset),2)
else: # Report temp in F if unit selected in settings
self.value = round((9.0 / 5.0 * self.t.value + 32 + self.offset), 2)
self.log_data(self.value) self.log_data(self.value)
self.push_update(self.value) self.push_update(self.value)