Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ verify_ssl = true
[dev-packages]
pytest = "*"
pytest-only = "*"
fake-rpigpio = "*"

[packages]
paho-mqtt = "*"
Expand Down
33 changes: 24 additions & 9 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 23 additions & 9 deletions christmas_light_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@
import paho.mqtt.client as mqtt
import configargparse

import RPi.GPIO as GPIO
try:
import RPi.GPIO
except (RuntimeError, ModuleNotFoundError):
print('Using Fake RPi GPIO Imports')
from fake_rpigpio import utils, RPi
import sys
utils.install()
sys.modules['RPi'] = RPi

from src.devices_parser import DevicesParser
from src.ir_device import IRDevice
from src.homeassistant import expose_devices

# Create the logger
logger = logging.getLogger()
Expand All @@ -30,6 +38,10 @@
rfDeviceConfig.add_argument('--rf_gpio_pin', help='The pin where the RF - Transmitter is connected to', type=int, default=17)
rfDeviceConfig.add_argument('-e', '--rf_enable_pin', help='If your Transmitter has an optional enable pin, this needs to be set', type=int, nargs=1)

homeassistantConfig = argParser.add_argument_group('HomeAssistant', description='Arguments used to configure the connection to homeassistant')
homeassistantConfig.add_argument('--expose_to_homeassistant', default=False, action='store_true', help='If set, all devices read from the devices.yaml file will be exposed to homeassistant via the configured MQTT broker.')
homeassistantConfig.add_argument('--discovery_topic', type=str, default='homeassistant', help='Root Topic name for the homeassistant discovery. Set if it needs to be changed.')

# Parse the command line arguments
args = argParser.parse_args()
if not args.debug:
Expand All @@ -49,15 +61,15 @@
logger.exception(ex)
exit(1)

# Initialize the GPIO pins
GPIO.setmode(GPIO.BCM)
# Initialize the RPi.GPIO pins
RPi.GPIO.setmode(RPi.GPIO.BCM)

if args.rf_enable_pin:
pin = args.rf_enable_pin[0]
logger.info(f'Enable Pin for the RF - Devices was set to pin {pin}')
logger.debug(f'Setting pin {pin} as OUTPUT')
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, GPIO.LOW)
RPi.GPIO.setup(pin, RPi.GPIO.OUT)
RPi.GPIO.output(pin, RPi.GPIO.LOW)

def on_connect(client, userdata, flags, rc):
logger.info('Sucessfully connected to the mqtt broker')
Expand All @@ -66,6 +78,10 @@ def on_connect(client, userdata, flags, rc):
logger.debug(f'Subscribing to root topic: {topic}')
client.subscribe(topic)

if args.expose_to_homeassistant:
logger.info('Exposing all PowerPlug Devices to Homeassistant')
expose_devices(devDict, client, root_topic=args.topic, discovery_topic=args.discovery_topic)

def on_message(client, userdata, message):
logger.debug('Received mqtt messaged from the broker')
logger.debug('Topic: {}'.format(message.topic))
Expand Down Expand Up @@ -107,7 +123,5 @@ def on_message(client, userdata, message):
logger.error('An unexpected error occured: {}'.format(ex))
logger.exception(ex)
finally:
logger.debug('Cleaning up GPIO Channels')

# TODO: I need some sort of cleanup function in the device classes.
GPIO.cleanup()
logger.debug('Cleaning up RPi.GPIO Channels')
RPi.GPIO.cleanup()
1 change: 1 addition & 0 deletions config.conf
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
rf_gpio_pin = 22
rf_enable_pin = 27
expose_to_homeassistant = True
3 changes: 3 additions & 0 deletions devices.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
fiberglaslampe:
type: "PowerPlug"
friendlyName: "Fieberglas Lampe"
codes:
- 5825904
- 5888928
pulselength: 510
protocol: 5

blauelampe:
friendlyName: "Blaue Lampe"
type: "PowerPlug"
codes:
- 5825908
Expand All @@ -15,6 +17,7 @@ blauelampe:
protocol: 5

grauelampe:
friendlyName: "Graue Lampe"
type: "PowerPlug"
codes:
- 5825916
Expand Down
28 changes: 28 additions & 0 deletions homeassistant/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
services:
homeassistant:
image: homeassistant/home-assistant:latest
ports:
- "8123:8123"
volumes:
- "homeassistant_volume:/config"
networks:
- homeassistant_network

mqttbroker:
image: eclipse-mosquitto
expose:
- 1883
- 1884
ports:
- "1883:1883"
- "1884:1884"
networks:
- homeassistant_network
command: "mosquitto -c /mosquitto-no-auth.conf"

volumes:
homeassistant_volume:

networks:
homeassistant_network:

10 changes: 9 additions & 1 deletion src/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,24 @@
"""

class Device():
def __init__(self, name: str):
def __init__(self, name: str, friendlyName: str = ''):
self.__name = name
self.__friendlyName = friendlyName

def getName(self):
return self.__name

def setName(self, newName: str):
self.__name = newName

def getFriendlyName(self):
return self.__friendlyName

def setFriendlyName(self, newFriendlyName: str):
self.__friendlyName = newFriendlyName

name = property(getName, setName)
friendlyName = property(getFriendlyName, setFriendlyName)

def turnOn(self):
raise NotImplementedError('The devices subclass has to implement this function')
Expand Down
25 changes: 13 additions & 12 deletions src/devices_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,13 @@ def parseDevicesFile(self, args, devicesFilePath=''):
self.__logger.exception('Error while trying to open the devices file: {}'.format(ioErr))
raise

parsedDevicesuration = None
devicesYaml = None
try:
parsedDevicesuration = yaml.load(fileContent, Loader=yaml.CLoader)
print(parsedDevicesuration)
devicesYaml = yaml.load(fileContent, Loader=yaml.CLoader)
except yaml.ScannerError as err:
self.__logger.error('Failed to read the devices file: {}'.format(err))
raise
return self.__parseTypes(parsedDevicesuration, args)
return self.__parseTypes(devicesYaml, args)

def __parseTypes(self, parsedDevicesFile, args):
"""
Expand All @@ -50,21 +49,22 @@ def __parseTypes(self, parsedDevicesFile, args):
for curDevName, curDev in parsedDevicesFile.items():
try:
devType = curDev['type']
friendlyName = curDev.get('friendlyName', curDevName)

if devType == 'PowerPlug':
parsedPlug = self.__parsePowerPlug(curDev, curDevName, args)
parsedPlug = self.__parsePowerPlug(curDev, curDevName, friendlyName, args)

if parsedPlug is not None:
outDict[curDevName] = parsedPlug

elif devType == 'GPIODevice':
parsedGpioDev = self.__parseGpioDevice(curDev, curDevName)
parsedGpioDev = self.__parseGpioDevice(curDev, curDevName, friendlyName)

if parsedGpioDev is not None:
outDict[curDevName] = parsedGpioDev

elif devType == 'IRDevice':
parsedIrDev = self.__parseIrDevice(curDev, curDevName)
parsedIrDev = self.__parseIrDevice(curDev, curDevName, friendlyName)

if parsedIrDev is not None:
outDict[curDevName] = parsedIrDev
Expand All @@ -80,7 +80,7 @@ def __parseTypes(self, parsedDevicesFile, args):

return outDict

def __parsePowerPlug(self, powerPlugRaw, devName, args):
def __parsePowerPlug(self, powerPlugRaw, devName, friendlyName, args):
"""
Returns the PowerPlug object parsed from the passed object model.
"""
Expand Down Expand Up @@ -108,6 +108,7 @@ def __parsePowerPlug(self, powerPlugRaw, devName, args):
if args.rf_enable_pin:
outPlug = PowerPlug(onCodes, offCodes,\
name=devName,\
friendlyName=friendlyName,\
senderGpioPin=args.rf_gpio_pin,\
pulselength=pulselength,\
protocol=protocol,\
Expand All @@ -126,22 +127,22 @@ def __parsePowerPlug(self, powerPlugRaw, devName, args):

return outPlug

def __parseGpioDevice(self, gpioDeviceRaw, devName):
def __parseGpioDevice(self, gpioDeviceRaw, devName, friendlyName):
"""
Returns the GPIO object parsed from the passed object model.
"""
outGpioDevice = None
try:
pin = gpioDeviceRaw['pin']

outGpioDevice = GPIODevice(devName, pin)
outGpioDevice = GPIODevice(devName, pin, friendlyName)

except KeyError as kerr:
self.__logger.warning('Missing Property {}, skipping PowerPlug'.format(kerr))

return outGpioDevice

def __parseIrDevice(self, irDeviceRaw, devName):
def __parseIrDevice(self, irDeviceRaw, devName, friendlyName):
"""
Returns the parsed IR Device
"""
Expand All @@ -164,4 +165,4 @@ def __parseIrDevice(self, irDeviceRaw, devName):

kwargs[k] = int(v)

return IRDevice(devName, **kwargs)
return IRDevice(devName, friendlyName=friendlyName, **kwargs)
4 changes: 2 additions & 2 deletions src/gpio_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"""

class GPIODevice(Device):
def __init__(self, name: str, pin: int):
super().__init__(name)
def __init__(self, name: str, pin: int, friendlyName=''):
super().__init__(name, friendlyName=friendlyName)
self.__logger = logging.getLogger().getChild('GPIO Device')
self.__pin = pin

Expand Down
Loading