mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-11-10 01:17:42 +01:00
234 lines
12 KiB
Python
234 lines
12 KiB
Python
import asyncio
|
|
from asyncio import tasks
|
|
import logging
|
|
from cbpi.api import *
|
|
import aiohttp
|
|
from aiohttp import web
|
|
from cbpi.controller.fermentation_controller import FermentationController
|
|
from cbpi.api.dataclasses import Fermenter, Props, Step
|
|
from cbpi.api.base import CBPiBase
|
|
from cbpi.api.config import ConfigType
|
|
import json
|
|
import webbrowser
|
|
|
|
class FermenterAutostart(CBPiExtension):
|
|
|
|
def __init__(self,cbpi):
|
|
self.cbpi = cbpi
|
|
self._task = asyncio.create_task(self.run())
|
|
self.controller : FermentationController = cbpi.fermenter
|
|
|
|
async def run(self):
|
|
logging.info("Starting Fermenter Autorun")
|
|
#get all kettles
|
|
try:
|
|
self.fermenter = self.controller.get_state()
|
|
for id in self.fermenter['data']:
|
|
try:
|
|
self.autostart=(id['props']['AutoStart'])
|
|
if self.autostart == "Yes":
|
|
fermenter_id=(id['id'])
|
|
logging.info("Enabling Autostart for Fermenter {}".format(fermenter_id))
|
|
self.fermenter=self.cbpi.fermenter._find_by_id(fermenter_id)
|
|
try:
|
|
if (self.fermenter.instance is None or self.fermenter.instance.state == False):
|
|
await self.cbpi.fermenter.start(self.fermenter.id)
|
|
logging.info("Successfully switched on Ferenterlogic for Fermenter {}".format(self.fermenter.id))
|
|
except Exception as e:
|
|
logging.error("Failed to switch on FermenterLogic {} {}".format(self.fermenter.id, e))
|
|
except:
|
|
pass
|
|
except:
|
|
pass
|
|
|
|
|
|
@parameters([Property.Number(label="HeaterOffsetOn", configurable=True, description="Offset as decimal number when the heater is switched on. Should be greater then 'HeaterOffsetOff'. For example a value of 2 switches on the heater if the current temperature is 2 degrees below the target temperature"),
|
|
Property.Number(label="HeaterOffsetOff", configurable=True, description="Offset as decimal number when the heater is switched off. Should be smaller then 'HeaterOffsetOn'. For example a value of 1 switches off the heater if the current temperature is 1 degree below the target temperature"),
|
|
Property.Number(label="CoolerOffsetOn", configurable=True, description="Offset as decimal number when the cooler is switched on. Should be greater then 'CoolerOffsetOff'. For example a value of 2 switches on the cooler if the current temperature is 2 degrees above the target temperature"),
|
|
Property.Number(label="CoolerOffsetOff", configurable=True, description="Offset as decimal number when the cooler is switched off. Should be smaller then 'CoolerOffsetOn'. For example a value of 1 switches off the cooler if the current temperature is 1 degree above the target temperature"),
|
|
Property.Number(label="HeaterMaxPower", configurable=True,description="Max Power [%] for Heater (default: 100)"),
|
|
Property.Number(label="CoolerMaxPower", configurable=True ,description="Max Power [%] for Cooler (default: 100)"),
|
|
Property.Select(label="AutoStart", options=["Yes","No"],description="Autostart Fermenter on cbpi start"),
|
|
Property.Sensor(label="sensor2",description="Optional Sensor for LCDisplay(e.g. iSpindle)")])
|
|
|
|
class FermenterHysteresis(CBPiFermenterLogic):
|
|
|
|
async def run(self):
|
|
try:
|
|
self.heater_offset_min = float(self.props.get("HeaterOffsetOn", 0))
|
|
self.heater_offset_max = float(self.props.get("HeaterOffsetOff", 0))
|
|
self.cooler_offset_min = float(self.props.get("CoolerOffsetOn", 0))
|
|
self.cooler_offset_max = float(self.props.get("CoolerOffsetOff", 0))
|
|
self.heater_max_power = int(self.props.get("HeaterMaxPower", 100))
|
|
self.cooler_max_power = int(self.props.get("CoolerMaxPower", 100))
|
|
|
|
self.fermenter = self.get_fermenter(self.id)
|
|
self.heater = self.fermenter.heater
|
|
self.cooler = self.fermenter.cooler
|
|
|
|
heater = self.cbpi.actor.find_by_id(self.heater)
|
|
cooler = self.cbpi.actor.find_by_id(self.cooler)
|
|
|
|
while self.running == True:
|
|
|
|
sensor_value = float(self.get_sensor_value(self.fermenter.sensor).get("value"))
|
|
target_temp = float(self.get_fermenter_target_temp(self.id))
|
|
|
|
try:
|
|
heater_state = heater.instance.state
|
|
except:
|
|
heater_state= False
|
|
try:
|
|
cooler_state = cooler.instance.state
|
|
except:
|
|
cooler_state= False
|
|
|
|
if sensor_value + self.heater_offset_min <= target_temp:
|
|
if self.heater and (heater_state == False):
|
|
await self.actor_on(self.heater, self.heater_max_power)
|
|
|
|
if sensor_value + self.heater_offset_max >= target_temp:
|
|
if self.heater and (heater_state == True):
|
|
await self.actor_off(self.heater)
|
|
|
|
if sensor_value >= self.cooler_offset_min + target_temp:
|
|
if self.cooler and (cooler_state == False):
|
|
await self.actor_on(self.cooler, self.cooler_max_power)
|
|
|
|
if sensor_value <= self.cooler_offset_max + target_temp:
|
|
if self.cooler and (cooler_state == True):
|
|
await self.actor_off(self.cooler)
|
|
|
|
await asyncio.sleep(1)
|
|
|
|
except asyncio.CancelledError as e:
|
|
pass
|
|
except Exception as e:
|
|
logging.error("Fermenter Hysteresis Error {}".format(e))
|
|
finally:
|
|
self.running = False
|
|
if self.heater:
|
|
await self.actor_off(self.heater)
|
|
if self.cooler:
|
|
await self.actor_off(self.cooler)
|
|
|
|
|
|
@parameters([Property.Number(label="HeaterOffsetOn", configurable=True, description="Offset as decimal number when the heater is switched on. Should be greater then 'HeaterOffsetOff'. For example a value of 2 switches on the heater if the current temperature is 2 degrees below the target temperature"),
|
|
Property.Number(label="HeaterOffsetOff", configurable=True, description="Offset as decimal number when the heater is switched off. Should be smaller then 'HeaterOffsetOn'. For example a value of 1 switches off the heater if the current temperature is 1 degree below the target temperature"),
|
|
Property.Number(label="CoolerOffsetOn", configurable=True, description="Offset as decimal number when the cooler is switched on. Should be greater then 'CoolerOffsetOff'. For example a value of 2 switches on the cooler if the current temperature is 2 degrees below the target temperature"),
|
|
Property.Number(label="CoolerOffsetOff", configurable=True, description="Offset as decimal number when the cooler is switched off. Should be smaller then 'CoolerOffsetOn'. For example a value of 1 switches off the cooler if the current temperature is 1 degree below the target temperature"),
|
|
Property.Number(label="SpundingOffsetOpen", configurable=True, description="Offset above target pressure as decimal number when the valve is opened"),
|
|
Property.Select(label="ValveRelease", options=[1,2,3,4,5],description="Valve Release time in seconds"),
|
|
Property.Select(label="Pause", options=[1,2,3,4,5],description="Pause time in seconds between valve release"),
|
|
Property.Select(label="AutoStart", options=["Yes","No"],description="Autostart Fermenter on cbpi start"),
|
|
Property.Sensor(label="sensor2",description="Optional Sensor for LCDisplay(e.g. iSpindle)")])
|
|
|
|
class FermenterSpundingHysteresis(CBPiFermenterLogic):
|
|
# subroutine that controls pressure
|
|
async def pressure_control(self):
|
|
self.spunding_offset=float(self.props.get("SpundingOffsetOpen",0))
|
|
self.valverelease=int(self.props.get("ValveRelease",1))
|
|
self.pause=int(self.props.get("Pause",2))
|
|
if self.valve and self.fermenter.pressure_sensor:
|
|
#valve = self.cbpi.actor.find_by_id(self.valve)
|
|
|
|
await self.actor_off(self.valve)
|
|
#logging.info("Closing Spunding Valve")
|
|
|
|
while self.running:
|
|
target_pressure=float(self.fermenter.target_pressure)
|
|
current_pressure = float(self.get_sensor_value(self.fermenter.pressure_sensor).get("value"))
|
|
#logging.info(f'Target: {target_pressure} | Current: {current_pressure}')
|
|
if current_pressure >= (target_pressure + self.spunding_offset) and target_pressure !=0:
|
|
while current_pressure >= target_pressure:
|
|
await self.actor_on(self.valve)
|
|
await asyncio.sleep(self.valverelease)
|
|
await self.actor_off(self.valve)
|
|
await asyncio.sleep(self.pause)
|
|
current_pressure = float(self.get_sensor_value(self.fermenter.pressure_sensor).get("value"))
|
|
#logging.info("Value higher than target: Spunding loop is running")
|
|
|
|
await asyncio.sleep(1)
|
|
else:
|
|
logging.info("No valve or pressure sensor defined")
|
|
|
|
async def temperature_control(self):
|
|
self.heater_offset_min = float(self.props.get("HeaterOffsetOn", 0))
|
|
self.heater_offset_max = float(self.props.get("HeaterOffsetOff", 0))
|
|
self.cooler_offset_min = float(self.props.get("CoolerOffsetOn", 0))
|
|
self.cooler_offset_max = float(self.props.get("CoolerOffsetOff", 0))
|
|
|
|
heater = self.cbpi.actor.find_by_id(self.heater)
|
|
cooler = self.cbpi.actor.find_by_id(self.cooler)
|
|
|
|
while self.running == True:
|
|
|
|
sensor_value = float(self.get_sensor_value(self.fermenter.sensor).get("value"))
|
|
target_temp = float(self.get_fermenter_target_temp(self.id))
|
|
|
|
try:
|
|
heater_state = heater.instance.state
|
|
except:
|
|
heater_state= False
|
|
try:
|
|
cooler_state = cooler.instance.state
|
|
except:
|
|
cooler_state= False
|
|
|
|
if sensor_value + self.heater_offset_min <= target_temp:
|
|
if self.heater and (heater_state == False):
|
|
await self.actor_on(self.heater)
|
|
|
|
if sensor_value + self.heater_offset_max >= target_temp:
|
|
if self.heater and (heater_state == True):
|
|
await self.actor_off(self.heater)
|
|
|
|
if sensor_value >= self.cooler_offset_min + target_temp:
|
|
if self.cooler and (cooler_state == False):
|
|
await self.actor_on(self.cooler)
|
|
|
|
if sensor_value <= self.cooler_offset_max + target_temp:
|
|
if self.cooler and (cooler_state == True):
|
|
await self.actor_off(self.cooler)
|
|
|
|
await asyncio.sleep(1)
|
|
|
|
async def run(self):
|
|
try:
|
|
self.fermenter = self.get_fermenter(self.id)
|
|
self.heater = self.fermenter.heater
|
|
self.cooler = self.fermenter.cooler
|
|
self.valve = self.fermenter.valve
|
|
|
|
pressure_controller = asyncio.create_task(self.pressure_control())
|
|
temperature_controller = asyncio.create_task(self.temperature_control())
|
|
|
|
await pressure_controller
|
|
await temperature_controller
|
|
|
|
except asyncio.CancelledError as e:
|
|
pass
|
|
except Exception as e:
|
|
logging.error("Fermenter Spunding Hysteresis Error {}".format(e))
|
|
finally:
|
|
self.running = False
|
|
if self.heater:
|
|
await self.actor_off(self.heater)
|
|
if self.cooler:
|
|
await self.actor_off(self.cooler)
|
|
if self.valve:
|
|
await self.actor_off(self.valve)
|
|
|
|
def setup(cbpi):
|
|
|
|
'''
|
|
This method is called by the server during startup
|
|
Here you need to register your plugins at the server
|
|
|
|
:param cbpi: the cbpi core
|
|
:return:
|
|
'''
|
|
cbpi.plugin.register("Fermenter Spunding Hysteresis", FermenterSpundingHysteresis)
|
|
cbpi.plugin.register("Fermenter Hysteresis", FermenterHysteresis)
|
|
cbpi.plugin.register("Fermenter Autostart", FermenterAutostart)
|
|
|