Skip to content

Instantly share code, notes, and snippets.

@bbartling
Last active February 9, 2024 17:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bbartling/573030a2facc97e178b2362606b9cdda to your computer and use it in GitHub Desktop.
Save bbartling/573030a2facc97e178b2362606b9cdda to your computer and use it in GitHub Desktop.
bacpypes3 example of a BACnet server ran Docker on rasp pi hardware with Alpine Linux. App is a make over of legacy bacpypes mini_device.py from repo samples directory but in bacpypes3 asyncio version
# Use an Alpine Linux base image for Raspberry Pi
FROM arm32v7/alpine:latest
# Set the working directory in the container
WORKDIR /app
# Install necessary packages and dependencies
RUN apk --no-cache add python3 python3-dev py3-pip
# Install BACpypes and any other required dependencies
RUN pip3 install bacpypes3
# Copy your Python application into the container
COPY mini_device_revisited.py /app/mini_device_revisited.py
# Expose the UDP port 47808
EXPOSE 47808/udp
# Define the command to run your BACpypes application
# CMD ["python3", "/app/mini_device_revisited.py", "--debug", "bacpypes3.ipv4.IPv4DatagramProtocol"]
CMD ["python3", "/app/mini_device_revisited.py", "--debug"]
import asyncio
from bacpypes3.debugging import ModuleLogger
from bacpypes3.argparse import SimpleArgumentParser
from bacpypes3.app import Application
from bacpypes3.local.analog import AnalogValueObject
from bacpypes3.local.binary import BinaryValueObject
from bacpypes3.local.analog import AnalogValueObject
from bacpypes3.local.cmd import Commandable
class CommandableAnalogValueObject(Commandable, AnalogValueObject):
"""
Commandable Analog Value Object
"""
_debug = 0
_log = ModuleLogger(globals())
INTERVAL = 1.0
class SampleApplication:
def __init__(self, args, test_bv, test_av, commandable_analog_value):
# embed an application
self.app = Application.from_args(args)
# extract the kwargs that are special to this application
self.test_bv = test_bv
self.app.add_object(test_bv)
self.test_av = test_av
self.app.add_object(test_av)
self.commandable_analog_value = commandable_analog_value
self.app.add_object(commandable_analog_value)
# create a task to update the values
asyncio.create_task(self.update_values())
async def update_values(self):
test_values = [
("active", 1.0),
("inactive", 2.0),
("active", 3.0),
("inactive", 4.0),
]
while True:
await asyncio.sleep(INTERVAL)
next_value = test_values.pop(0)
test_values.append(next_value)
if _debug:
_log.debug(" - next_value: %r", next_value)
_log.debug(
"commandable_analog_value: %r",
self.commandable_analog_value.presentValue,
)
self.test_av.presentValue = next_value[1]
self.test_bv.presentValue = next_value[0]
async def main():
args = SimpleArgumentParser().parse_args()
if _debug:
_log.debug("args: %r", args)
# define BACnet objects
test_av = AnalogValueObject(
objectIdentifier=("analogValue", 1),
objectName="av",
presentValue=0.0,
statusFlags=[0, 0, 0, 0],
covIncrement=1.0,
)
_log.debug(" - test_av: %r", test_av)
test_bv = BinaryValueObject(
objectIdentifier=("binaryValue", 1),
objectName="bv",
presentValue="inactive",
statusFlags=[0, 0, 0, 0],
)
_log.debug(" - test_bv: %r", test_bv)
# Create an instance of your commandable object
commandable_analog_value = CommandableAnalogValueObject(
objectIdentifier=("analogValue", 3),
objectName="commandable-av",
presentValue=-1.0,
statusFlags=[0, 0, 0, 0],
covIncrement=1.0,
description="Commandable analog value object",
)
# instantiate the SampleApplication with test_av and test_bv
app = SampleApplication(
args,
test_av=test_av,
test_bv=test_bv,
commandable_analog_value=commandable_analog_value,
)
if _debug:
_log.debug("app: %r", app)
await asyncio.Future()
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
if _debug:
_log.debug("keyboard interrupt")
# build docker file with:
sudo docker build -t bacpypes-three-mini-device-revisited .
# run docker file:
sudo docker run -d -p 47808:47808/udp --name my-bacpypes-container bacpypes-three-mini-device-revisited
sudo docker run -d --network="host" --name my-bacpypes-container bacnet-server
# stop docker container:
sudo docker stop my-bacpypes-container
# remove docker container if you want to make change to .py file rebuild and run again:
sudo docker rm my-bacpypes-container
# view logs:
sudo docker logs my-bacpypes-container
# tail logs:
sudo docker logs -f my-bacpypes-container
# pipe logs to less:
sudo docker logs my-bacpypes-container | less
# pip install ifaddr
# verify app is listening on port 47808
$ ss --listening --udp --process | grep 47808
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment