Merge pull request #18 from avollkopf/development

Merge from Development
This commit is contained in:
Alexander Vollkopf 2021-12-06 17:21:45 +01:00 committed by GitHub
commit d8e8e49ed0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 219 additions and 83 deletions

50
.dockerignore Normal file
View file

@ -0,0 +1,50 @@
# Docker
docker-compose.yml
.docker
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
**/__pycache__
**/*.py[cod]
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Virtual environment
.env/
.venv/
venv/
venv3/
*.cover
*.log
.git
.mypy_cache
.pytest_cache
.hypothesis
.idea
**/*.swp

View file

@ -54,11 +54,17 @@ jobs:
PUBLISH_IMAGE=false PUBLISH_IMAGE=false
TAGS="${{ env.image-name }}:dev" TAGS="${{ env.image-name }}:dev"
# Define the image that will be used as a cached image
# to speed up the build process
BUILD_CACHE_IMAGE_NAME=${TAGS}
if [[ $GITHUB_REF_NAME == master ]] || [[ $GITHUB_REF_NAME == main ]]; then if [[ $GITHUB_REF_NAME == master ]] || [[ $GITHUB_REF_NAME == main ]]; then
# when building master/main use :latest tag and the version number # when building master/main use :latest tag and the version number
# from the cbpi/__init__.py file # from the cbpi/__init__.py file
VERSION=$(grep -o -E "(([0-9]{1,2}[.]?){3}[0-9]+)" cbpi/__init__.py) VERSION=$(grep -o -E "(([0-9]{1,2}[.]?){3}[0-9]+)" cbpi/__init__.py)
TAGS="${{ env.image-name }}:latest,${{ env.image-name }}:v${VERSION}" LATEST_IMAGE=${{ env.image-name }}:latest
BUILD_CACHE_IMAGE_NAME=${LATEST_IMAGE}
TAGS="${LATEST_IMAGE},${{ env.image-name }}:v${VERSION}"
PUBLISH_IMAGE=true PUBLISH_IMAGE=true
elif [[ $GITHUB_REF_NAME == development ]]; then elif [[ $GITHUB_REF_NAME == development ]]; then
PUBLISH_IMAGE=true PUBLISH_IMAGE=true
@ -67,6 +73,7 @@ jobs:
# Set output parameters. # Set output parameters.
echo ::set-output name=tags::${TAGS} echo ::set-output name=tags::${TAGS}
echo ::set-output name=publish_image::${PUBLISH_IMAGE} echo ::set-output name=publish_image::${PUBLISH_IMAGE}
echo ::set-output name=build_cache_image_name::${BUILD_CACHE_IMAGE_NAME}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@master uses: docker/setup-qemu-action@master
@ -91,8 +98,11 @@ jobs:
context: . context: .
file: ./Dockerfile file: ./Dockerfile
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
target: deploy
push: ${{ steps.prep.outputs.publish_image }} push: ${{ steps.prep.outputs.publish_image }}
tags: ${{ steps.prep.outputs.tags }} tags: ${{ steps.prep.outputs.tags }}
cache-from: type=registry,ref=${{ steps.prep.outputs.build_cache_image_name }}
cache-to: type=inline
labels: | labels: |
org.opencontainers.image.title=${{ github.event.repository.name }} org.opencontainers.image.title=${{ github.event.repository.name }}
org.opencontainers.image.description=${{ github.event.repository.description }} org.opencontainers.image.description=${{ github.event.repository.description }}

View file

@ -3,7 +3,7 @@ RUN apk --no-cache add curl && mkdir /downloads
# Download installation files # Download installation files
RUN curl https://github.com/avollkopf/craftbeerpi4-ui/archive/main.zip -L -o ./downloads/cbpi-ui.zip RUN curl https://github.com/avollkopf/craftbeerpi4-ui/archive/main.zip -L -o ./downloads/cbpi-ui.zip
FROM python:3.7 FROM python:3.7 as base
# Install dependencies # Install dependencies
RUN apt-get update \ RUN apt-get update \
@ -14,28 +14,44 @@ RUN apt-get install --no-install-recommends -y \
python3-pip \ python3-pip \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN python -m pip install --upgrade pip setuptools wheel ENV VIRTUAL_ENV=/opt/venv
WORKDIR /cbpi
# Create non-root user working directory # Create non-root user working directory
RUN groupadd -g 1000 -r craftbeerpi \ RUN groupadd -g 1000 -r craftbeerpi \
&& useradd -u 1000 -r -s /bin/false -g craftbeerpi craftbeerpi \ && useradd -u 1000 -r -s /bin/false -g craftbeerpi craftbeerpi \
&& chown craftbeerpi:craftbeerpi /cbpi && mkdir /cbpi \
&& chown craftbeerpi:craftbeerpi /cbpi \
# Install craftbeerpi from source && mkdir -p $VIRTUAL_ENV \
COPY . /cbpi-src && chown -R craftbeerpi:craftbeerpi ${VIRTUAL_ENV}
RUN pip3 install --no-cache-dir /cbpi-src
# Install craftbeerpi-ui
COPY --from=download /downloads /downloads
RUN pip3 install --no-cache-dir /downloads/cbpi-ui.zip
# Clean up installation files
RUN rm -rf /downloads /cbpi-src
USER craftbeerpi USER craftbeerpi
RUN cbpi setup # create virtual environment
RUN python3 -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
RUN python3 -m pip install --no-cache-dir --upgrade pip setuptools wheel
# Install craftbeerpi requirements for better caching
COPY --chown=craftbeerpi ./requirements.txt /cbpi-src/
RUN pip3 install --no-cache-dir -r /cbpi-src/requirements.txt
FROM base as deploy
# Install craftbeerpi from source
COPY --chown=craftbeerpi . /cbpi-src
RUN pip3 install --no-cache-dir /cbpi-src
# Install craftbeerpi-ui
COPY --from=download --chown=craftbeerpi /downloads /downloads
RUN pip3 install --no-cache-dir /downloads/cbpi-ui.zip
# Clean up installation files
USER root
RUN rm -rf /downloads /cbpi-src
USER craftbeerpi
WORKDIR /cbpi
RUN ["cbpi", "setup"]
EXPOSE 8000 EXPOSE 8000

View file

@ -1 +1 @@
__version__ = "4.0.0.55" __version__ = "4.0.0.56"

View file

@ -102,7 +102,7 @@ class BasicController:
logging.info("{} started {}".format(self.name, id)) logging.info("{} started {}".format(self.name, id))
await self.push_udpate() # await self.push_udpate()
except Exception as e: except Exception as e:
logging.error("{} Cant start {} - {}".format(self.name, id, e)) logging.error("{} Cant start {} - {}".format(self.name, id, e))

View file

@ -33,24 +33,24 @@ class SatelliteController:
if self.client is not None and self.client._connected: if self.client is not None and self.client._connected:
try: try:
await self.client.publish(topic, message, qos=1, retain=retain) await self.client.publish(topic, message, qos=1, retain=retain)
except: except Exception as e:
self.logger.warning("Failed to push data via mqtt") self.logger.warning("Failed to push data via mqtt: {}".format(e))
async def _actor_on(self, messages): async def _actor_on(self, messages):
async for message in messages: async for message in messages:
try: try:
topic_key = message.topic.split("/") topic_key = message.topic.split("/")
await self.cbpi.actor.on(topic_key[2]) await self.cbpi.actor.on(topic_key[2])
except: except Exception as e:
self.logger.warning("Failed to process actor on via mqtt") self.logger.warning("Failed to process actor on via mqtt: {}".format(e))
async def _actor_off(self, messages): async def _actor_off(self, messages):
async for message in messages: async for message in messages:
try: try:
topic_key = message.topic.split("/") topic_key = message.topic.split("/")
await self.cbpi.actor.off(topic_key[2]) await self.cbpi.actor.off(topic_key[2])
except: except Exception as e:
self.logger.warning("Failed to process actor off via mqtt") self.logger.warning("Failed to process actor off via mqtt: {}".format(e))
async def _actor_power(self, messages): async def _actor_power(self, messages):
async for message in messages: async for message in messages:

View file

@ -1,61 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import asyncio
import json
from cbpi.api import parameters, Property, CBPiActor
from cbpi.api import * from cbpi.api import *
from .mqtt_actor import MQTTActor
@parameters([Property.Text(label="Topic", configurable=True, description = "MQTT Topic")]) from .generic_mqtt_actor import GenericMqttActor
class MQTTActor(CBPiActor): from .tasmota_mqtt_actor import TasmotaMqttActor
# Custom property which can be configured by the user
@action("Set Power", parameters=[Property.Number(label="Power", configurable=True,description="Power Setting [0-100]")])
async def setpower(self,Power = 100 ,**kwargs):
self.power=round(Power)
if self.power < 0:
self.power = 0
if self.power > 100:
self.power = 100
await self.set_power(self.power)
def __init__(self, cbpi, id, props):
super(MQTTActor, self).__init__(cbpi, id, props)
async def on_start(self):
self.topic = self.props.get("Topic", None)
self.power = 100
async def on(self, power=None):
if power is not None:
if power != self.power:
power = min(100, power)
power = max(0, power)
self.power = round(power)
await self.cbpi.satellite.publish(self.topic, json.dumps(
{"state": "on", "power": self.power}), True)
self.state = True
pass
async def off(self):
self.state = False
await self.cbpi.satellite.publish(self.topic, json.dumps(
{"state": "off", "power": self.power}), True)
pass
async def run(self):
while self.running:
await asyncio.sleep(1)
def get_state(self):
return self.state
async def set_power(self, power):
self.power = round(power)
if self.state == True:
await self.on(power)
else:
await self.off()
await self.cbpi.actor.actor_update(self.id,power)
pass
def setup(cbpi): def setup(cbpi):
''' '''
@ -67,3 +14,5 @@ def setup(cbpi):
''' '''
if str(cbpi.static_config.get("mqtt", False)).lower() == "true": if str(cbpi.static_config.get("mqtt", False)).lower() == "true":
cbpi.plugin.register("MQTTActor", MQTTActor) cbpi.plugin.register("MQTTActor", MQTTActor)
cbpi.plugin.register("MQTT Actor (Generic)", GenericMqttActor)
cbpi.plugin.register("MQTT Actor (Tasmota)", TasmotaMqttActor)

View file

@ -0,0 +1,37 @@
from cbpi.api import parameters, Property
from . import MQTTActor
@parameters([
Property.Text(label="Topic", configurable=True, description = "MQTT Topic"),
Property.Text(label="Payload", configurable=True, description = "Payload that is sent as MQTT message. Available placeholders are {switch_onoff}: [on|off], {switch_10}: [1|0], {power}: [0-100].")
])
class GenericMqttActor(MQTTActor):
def __init__(self, cbpi, id, props):
MQTTActor.__init__(self, cbpi, id, props)
self.payload = ""
async def on_start(self):
await MQTTActor.on_start(self)
self.payload = self.props.get("Payload", "{{\"state\": \"{switch_onoff}\", \"power\": {power}}}")
def normalize_power_value(self, power):
if power is not None:
if power != self.power:
power = min(100, power)
power = max(0, power)
self.power = round(power)
async def publish_mqtt_message(self, topic, payload):
self.logger.info("Publish '{payload}' to '{topic}'".format(payload = payload, topic = self.topic))
await self.cbpi.satellite.publish(self.topic, payload, True)
async def on(self, power=None):
self.normalize_power_value(power)
formatted_payload = self.payload.format(switch_onoff = "on", switch_10 = 1, power = self.power)
await self.publish_mqtt_message(self.topic, formatted_payload)
self.state = True
async def off(self):
formatted_payload = self.payload.format(switch_onoff = "off", switch_10 = 0, power = self.power)
await self.publish_mqtt_message(self.topic, formatted_payload)
self.state = False

View file

@ -0,0 +1,57 @@
import asyncio
import json
from cbpi.api import parameters, Property, CBPiActor
from cbpi.api import *
@parameters([Property.Text(label="Topic", configurable=True, description = "MQTT Topic")])
class MQTTActor(CBPiActor):
# Custom property which can be configured by the user
@action("Set Power", parameters=[Property.Number(label="Power", configurable=True, description="Power Setting [0-100]")])
async def setpower(self,Power = 100 ,**kwargs):
self.power=round(Power)
if self.power < 0:
self.power = 0
if self.power > 100:
self.power = 100
await self.set_power(self.power)
def __init__(self, cbpi, id, props):
super(MQTTActor, self).__init__(cbpi, id, props)
async def on_start(self):
self.topic = self.props.get("Topic", None)
self.power = 100
async def on(self, power=None):
if power is not None:
if power != self.power:
power = min(100, power)
power = max(0, power)
self.power = round(power)
await self.cbpi.satellite.publish(self.topic, json.dumps(
{"state": "on", "power": self.power}), True)
self.state = True
pass
async def off(self):
self.state = False
await self.cbpi.satellite.publish(self.topic, json.dumps(
{"state": "off", "power": self.power}), True)
pass
async def run(self):
while self.running:
await asyncio.sleep(1)
def get_state(self):
return self.state
async def set_power(self, power):
self.power = round(power)
if self.state == True:
await self.on(power)
else:
await self.off()
await self.cbpi.actor.actor_update(self.id,power)
pass

View file

@ -0,0 +1,13 @@
from cbpi.api import parameters, Property
from . import GenericMqttActor
@parameters([
Property.Text(label="Topic", configurable=True, description = "MQTT Topic"),
])
class TasmotaMqttActor(GenericMqttActor):
def __init__(self, cbpi, id, props):
GenericMqttActor.__init__(self, cbpi, id, props)
async def on_start(self):
await GenericMqttActor.on_start(self)
self.payload = "{switch_onoff}"

View file

@ -13,6 +13,10 @@ pyfiglet==0.8.post1
pandas==1.1.5 pandas==1.1.5
shortuuid==1.0.1 shortuuid==1.0.1
tabulate==0.8.7 tabulate==0.8.7
numpy==1.20.3
cbpi4ui cbpi4ui
click click==7.1.2
asyncio-mqtt importlib_metadata==4.8.2
asyncio-mqtt
psutil==5.8.0
zipp>=0.5