Skip to content
This repository has been archived by the owner on Dec 27, 2023. It is now read-only.

[WIP] Homie v3.0.0 #47

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ae5b02b
Move 'property' to its own file
bodiroga Oct 6, 2018
c4c304b
Move 'examples' to their own folder
bodiroga Oct 6, 2018
85ad939
Add a method to check if datatype is valid
bodiroga Oct 6, 2018
beb58a4
Add more attributes to HomieNodeProperty
bodiroga Oct 6, 2018
0113f9a
Update HomieNodePropertyRange class
bodiroga Oct 7, 2018
1cee0b2
Rename 'announce' methods to 'addProperty'
bodiroga Oct 7, 2018
e1b59ba
Return 'property' object in the addProperty method
bodiroga Oct 7, 2018
e2b2719
Rename 'setProperty' to 'getProperty'
bodiroga Oct 7, 2018
30a204e
Publish property attributes when nodes are published
bodiroga Oct 7, 2018
5f00be2
Rename 'sendProperties' to 'publishProperties'
bodiroga Oct 7, 2018
bce5fee
Properly implement fields getters and setters
bodiroga Oct 7, 2018
c12631d
Add validation functions for format and units
bodiroga Oct 7, 2018
f0df5cb
Fix error in isValidId function
bodiroga Oct 7, 2018
4eedb38
Improve format validation
bodiroga Oct 7, 2018
b9a285c
Check if datatype, format and unit are valid
bodiroga Oct 7, 2018
ff65f56
Fix property creation in node class
bodiroga Oct 7, 2018
4efbcea
Check if nodeId is valid
bodiroga Oct 7, 2018
8fe0097
Raise ValueError exception if property id is invalid
bodiroga Oct 7, 2018
c7685b9
Update the subscribe mechanism for properties
bodiroga Oct 7, 2018
9511fcf
Rename 'send' method to 'update'
bodiroga Oct 7, 2018
2d4cb76
Rework main file
bodiroga Oct 7, 2018
27581ad
Publish 'stats' information
bodiroga Oct 7, 2018
0f88fbf
Implement 'state' attributes
bodiroga Oct 7, 2018
840fde0
Implement 'name' attribute for nodes
bodiroga Oct 7, 2018
2dc3968
Update homie version to 3.0.0
bodiroga Oct 7, 2018
83e556b
Update all example files
bodiroga Oct 7, 2018
fcb9561
Update to Homie 3.0.1 and add more examples
bodiroga Dec 13, 2018
b4c4b41
Fix $stats mismatch with specification
bodiroga Jan 3, 2019
da3362b
Remove '' topic usage
bodiroga Mar 21, 2019
f63afa1
Remove "self.deviceID = None" statement
bodiroga Mar 21, 2019
5a8388d
Allow no format for certain datatypes
bodiroga Mar 21, 2019
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
*.pyc
*.sublime-*
__pycache__
build/*
dist/*
homie.egg-info/*
MANIFEST
22 changes: 11 additions & 11 deletions relay_switch.py → examples/relay_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@
logger = logging.getLogger(__name__)

config = homie.loadConfigFile("homie-python.json")
Homie = homie.Homie(config)
switchNode = Homie.Node("switch", "switch")
device = homie.Device(config)
switchNode = device.addNode("switch", "switch", "switch")
switchProperty = switchNode.addProperty("on")


def switchOnHandler(mqttc, obj, msg):
payload = msg.payload.decode("UTF-8").lower()
if payload == 'true':
def switchOnHandler(property, value):
if value == 'true':
logger.info("Switch: ON")
switchNode.setProperty("on").send("true")
property.update("true")
else:
logger.info("Switch: OFF")
switchNode.setProperty("on").send("false")
property.update("false")


def main():
Homie.setFirmware("relay-switch", "1.0.0")
switchNode.advertise("on").settable(switchOnHandler)
Homie.setup()
device.setFirmware("relay-switch", "1.0.0")
switchProperty.settable(switchOnHandler)

device.setup()

while True:
time.sleep(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,23 @@
"TOPIC": "homie"
}

Homie = homie.Homie(config)
switchNode = Homie.Node("switch", "switch")
device = homie.Device(config)
switchNode = device.addNode("switch", "switch", "switch")
switchProperty = switchNode.addProperty("on")


def switchOnHandler(mqttc, obj, msg):
payload = msg.payload.decode("UTF-8").lower()
if payload == 'true':
def switchOnHandler(property, value):
if value == 'true':
logger.info("Switch: ON")
switchNode.setProperty("on").send("true")
property.update("true")
else:
logger.info("Switch: OFF")
switchNode.setProperty("on").send("false")
property.update("false")


def main():
Homie.setFirmware("relay-switch", "1.0.0")
switchNode.advertise("on").settable(switchOnHandler)
Homie.setup()
device.setFirmware("relay-switch", "1.0.0")
switchProperty.settable(switchOnHandler)
device.setup()

while True:
time.sleep(1)
Expand Down
50 changes: 50 additions & 0 deletions examples/remote_with_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python
import time
import random
import homie
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

config = {
"HOST": "iot.eclipse.org",
"PORT": 1883,
"KEEPALIVE": 10,
"USERNAME": "",
"PASSWORD": "",
"CA_CERTS": "",
"DEVICE_ID": "remote-control",
"DEVICE_NAME": "xxxxxxxx",
"TOPIC": "homie"
}

device = homie.Device(config)
remoteNode = device.addNode("remote", "Remote", "Buttons")
playButtonProperty = remoteNode.addProperty("play", "Play Button", datatype="enum", format="PRESSED,RELEASED", retained=False)
nextButtonProperty = remoteNode.addProperty("next", "Next Button", datatype="enum", format="PRESSED,RELEASED", retained=False)
prevButtonProperty = remoteNode.addProperty("prev", "Prev Button", datatype="enum", format="PRESSED,RELEASED", retained=False)


def main():
device.setFirmware("remote-control", "1.0.0")

device.setup()

buttonPressFrecuency = 30
lastButtonPressTime = 0

while True:
# We simulate that the button is pressed (and released) every 30 seconds
if (time.time() - lastButtonPressTime) > buttonPressFrecuency:
playButtonProperty.update("PRESSED")
time.sleep(0.3)
playButtonProperty.update("RELEASED")
logger.info("Play button pressed and released")
lastButtonPressTime = time.time()
time.sleep(1)

if __name__ == '__main__':
try:
main()
except (KeyboardInterrupt, SystemExit):
logger.info("Quitting.")
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
TEMPERATURE_INTERVAL = 60

config = homie.loadConfigFile("homie-python.json")
Homie = homie.Homie(config)
temperatureNode = Homie.Node("temperature", "temperature")
device = homie.Device(config)
temperatureNode = device.addNode("temperature", "temperature", "temperature")
temperatureProperty = temperatureNode.addProperty("temperature", "Temperature value", "ºC", "float")

def read_temp_raw():
f = open(device_file, 'r')
Expand All @@ -41,15 +42,14 @@ def read_temp():
return temp_c

def main():
Homie.setFirmware("raspi-temperatureDS18B20", "1.0.0")
temperatureNode.advertise("degrees")
device.setFirmware("raspi-temperatureDS18B20", "1.0.0")

Homie.setup()
device.setup()

while True:
temperature = read_temp()
logger.info("Temperature: {:0.2f} °C".format(temperature))
temperatureNode.setProperty("degrees").send(temperature)
temperatureProperty.update(temperature)
time.sleep(TEMPERATURE_INTERVAL)

if __name__ == '__main__':
Expand Down
12 changes: 6 additions & 6 deletions temperature_raspi.py → examples/temperature_raspi.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
TEMPERATURE_INTERVAL = 60

config = homie.loadConfigFile("homie-python.json")
Homie = homie.Homie(config)
temperatureNode = Homie.Node("temperature", "temperature")
device = homie.Device(config)
temperatureNode = device.addNode("temperature", "temperature", "temperature")
temperatureProperty = temperatureNode.addProperty("temperature", "Temperature value", "ºC", "float")


def getCpuTemperature():
Expand All @@ -21,15 +22,14 @@ def getCpuTemperature():


def main():
Homie.setFirmware("raspi-temperature", "1.0.0")
temperatureNode.advertise("degrees")
device.setFirmware("raspi-temperature", "1.0.0")

Homie.setup()
device.setup()

while True:
temperature = getCpuTemperature()
logger.info("Temperature: {:0.2f} °C".format(temperature))
temperatureNode.setProperty("degrees").send(temperature)
temperatureProperty.update(temperature)
time.sleep(TEMPERATURE_INTERVAL)

if __name__ == '__main__':
Expand Down
20 changes: 10 additions & 10 deletions temperature_sensor.py → examples/temperature_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,30 @@
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

TEMPERATURE_INTERVAL = 3
TEMPERATURE_INTERVAL = 60

config = homie.loadConfigFile("homie-python.json")
Homie = homie.Homie(config)
temperatureNode = Homie.Node("temperature", "temperature")
humidityNode = Homie.Node("humidity", "humidity")
device = homie.Device(config)
temperatureNode = device.addNode("temperature", "temperature", "temperature")
humidityNode = device.addNode("humidity", "humidity", "humidity")
temperatureProperty = temperatureNode.addProperty("temperature", "Temperature value", "ºC", "float")
humidityProperty = humidityNode.addProperty("humidity", "Humidity value", "%", "float", "0.0:100.0")


def main():
Homie.setFirmware("awesome-temperature", "1.0.0")
temperatureNode.advertise("degrees")
humidityNode.advertise("humidity")
device.setFirmware("awesome-temperature", "1.0.0")

Homie.setup()
device.setup()

while True:
temperature = 22.0
humidity = 60.0

logger.info("Temperature: {:0.2f} °C".format(temperature))
temperatureNode.setProperty("degrees").send(temperature)
temperatureProperty.update(temperature)

logger.info("Humidity: {:0.2f} %".format(humidity))
humidityNode.setProperty("humidity").send(humidity)
humidityProperty.update(humidity)

time.sleep(TEMPERATURE_INTERVAL)

Expand Down
60 changes: 60 additions & 0 deletions examples/thermostat_with_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python
import time
import random
import homie
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

config = {
"HOST": "iot.eclipse.org",
"PORT": 1883,
"KEEPALIVE": 10,
"USERNAME": "",
"PASSWORD": "",
"CA_CERTS": "",
"DEVICE_ID": "xxxxxxxx",
"DEVICE_NAME": "xxxxxxxx",
"TOPIC": "homie"
}

device = homie.Device(config)
thermostatNode = device.addNode("thermostat", "Thermostat", "thermostat")
temperatureProperty = thermostatNode.addProperty("temperature", "Indoor Temperature", "ºC", "float", "0:50")
setpointProperty = thermostatNode.addProperty("setpoint", "Setpoint", "ºC", "float", "10:30")
modeProperty = thermostatNode.addProperty("mode", "Thermostat Mode", datatype="enum", format="NORMAL,COLD,HEAT")

def modeHandler(property, value):
logger.info("Changing thermostat mode to {}".format(value))
property.update(value)

def setpointHandler(property, value):
logger.info("Changing thermostat setpoint to {}ºC".format(value))
property.update(value)


def main():
device.setFirmware("thermostat", "1.0.0")
modeProperty.settable(modeHandler)
setpointProperty.settable(setpointHandler)

device.setup()

reportFrecuency = 60
lastTemperatureTime = 0

setpointProperty.update(21.5)
modeProperty.update("NORMAL")
while True:
if (time.time() - lastTemperatureTime) > reportFrecuency:
temperature = random.uniform(20,25)
temperatureProperty.update("{0:.2f}".format(temperature))
logger.info("New temperature value: {0:.2f}ºC".format(temperature))
lastTemperatureTime = time.time()
time.sleep(1)

if __name__ == '__main__':
try:
main()
except (KeyboardInterrupt, SystemExit):
logger.info("Quitting.")
46 changes: 45 additions & 1 deletion homie/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,53 @@ def generateDeviceId():
return "{:02x}".format(get_mac())


def isIdFormat(idString):
def isValidId(idString):
"""Validate device Id."""
logger.debug("isIdFormat")
if isinstance(idString, str):
r = re.compile('(^(?!\-)[a-z0-9\-]+(?<!\-)$)')
return True if r.match(idString) else False

def isValidDatatype(datatype):
"""Validate datatype"""
valid_datatypes = ("integer", "float", "boolean", "string", "enum", "color")
if datatype in valid_datatypes:
return True
return False

def isValidFormat(datatype, format):
"""Validate format"""
# False if there is a format without datatype
if not datatype and format:
return False
# True if there is no format but the datatype is "string", "integer", "float" or "boolean"
if not format and datatype in ("string", "integer", "float", "boolean"):
return True
if datatype == "color":
allowed_formats = ("rgb", "hsv")
return True if format in allowed_formats else False
elif datatype == "enum":
# Expression: value,value,value
# Examples: A,B,C or ON,OFF,PAUSE
r = re.compile(r'^(\w+,)*\w+$')
return True if r.match(format) else False
elif datatype == "integer":
# Expression: int:int
# Examples: 10:15 or -1:5
r = re.compile(r'^[-]?\d+[:][-]?\d+$')
return True if r.match(format) else False
elif datatype == "float":
# Expression: float:float
# Example: 3.14:15 or -1.1:-1.2
r = re.compile(r'^[+-]?([0-9]*[.])?[0-9]+[:][+-]?([0-9]*[.])?[0-9]+$')
return True if r.match(format) else False
return True

def isValidUnit(unit):
"""Validate unit"""
recommended_units = ("ºC", "ºF", "º", "L", "gal", "V", "W", "A", "%", "m",
"ft", "Pa", "psi", "#")
if unit not in recommended_units:
logger.warning("Unit '{}' is not one of the recommended ones".format(unit))
return True

Loading