Module homie_spec.properties
Exposes the Property class, Datatype and multiple partial constructors
Expand source code
"""
Exposes the Property class, Datatype and multiple partial constructors
"""
from enum import Enum, auto
from typing import NamedTuple, Callable, Optional, Iterable
from functools import partial
from homie_spec.messages import Message
class Datatype(Enum):
"Enum representation of the different values, the `$datatype` attribute can take"
INTEGER = auto()
FLOAT = auto()
BOOLEAN = auto()
STRING = auto()
ENUM = auto()
COLOR = auto()
@property
def payload(self) -> str:
"""
Serializes the object conforming it to be used inside a MQTT payload
>>> Datatype.INTEGER.payload
'integer'
>>> Datatype.FLOAT.payload
'float'
"""
return self.name.lower() # pylint: disable=no-member
__pdoc__ = {
"Datatype.INTEGER": "Integer type.",
"Datatype.FLOAT": "Float type.",
"Datatype.BOOLEAN": "Boolean type.",
"Datatype.STRING": "String type.",
"Datatype.ENUM": "Enum type.",
"Datatype.COLOR": "Color type.",
}
class Property(NamedTuple):
"Object representation of a property according to the Homie topology"
name: str
get: Callable[[], str]
datatype: Datatype = Datatype.STRING
set: Optional[Callable[[str], None]] = None
retained: Optional[bool] = None
settable: Optional[bool] = None
unit: Optional[str] = None
formatOf: Optional[str] = None
def messages(self, prefix: str) -> Iterable[Message]:
"""
Yields the messages from the property attributes.
All its messages are prefixed with its only parameter.
>>> prop = Property("A Property!", get=lambda: "hello world")
>>> next(prop.messages("/prefix")).payload
'A Property!'
"""
msg = partial(Message, prefix=prefix)
yield msg("$name", self.name)
yield msg("$datatype", self.datatype.payload)
if self.retained is not None:
retained = "true" if self.retained else "false"
yield msg("$retained", retained)
if self.settable is not None:
settable = "true" if self.settable else "false"
yield msg("$settable", settable)
if self.unit is not None:
yield msg("$unit", self.unit)
if self.formatOf is not None:
yield msg("$format", self.formatOf)
def getter_message(self, prefix: str) -> Message:
"""
Calls the property getter and returns its respective message
>>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER)
>>> msg = prop.getter_message("homie/device/node/prop")
>>> msg.topic
'homie/device/node/prop'
>>> msg.payload
'4'
"""
msg = partial(Message, prefix=prefix)
retained = True if self.retained is None else self.retained
return msg("", self.get(), retained=retained)
def setter_message(self, prefix: str, payload: str = "") -> Optional[Message]:
"""
Creates the necessary message to call the property setter.
Returns None if the property is not settable.
>>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER, settable=False)
>>> prop.setter_message("homie/device/node/prop", "2") is None
True
>>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER, settable=True)
>>> msg = prop.setter_message("homie/device/node/prop", "2")
>>> msg.topic
'homie/device/node/prop/set'
>>> msg.payload
'2'
"""
if self.settable is not True:
return None
msg = partial(Message, prefix=prefix)
return msg("set", payload)
PercentageProperty = partial( # pylint: disable=invalid-name
Property, datatype=Datatype.INTEGER, formatOf="0:100", unit="%"
)
BooleanProperty = partial( # pylint: disable=invalid-name
Property, datatype=Datatype.BOOLEAN
)
Property.name.__doc__ = "Friendly name of the property."
Property.datatype.__doc__ = "The property's datatype."
Property.formatOf.__doc__ = "Specifies restrictions or options for the given data type"
Property.settable.__doc__ = "Settable (true). Default is read-only (false)"
Property.retained.__doc__ = "Non-retained (false). The spec Default is Retained (true)."
Property.unit.__doc__ = "Optional unit of this property. Should follow a accepted value."
RECOMMENDED_UNITS = [
"°C",
"°F",
"°",
"L",
"gal",
"V",
"W",
"A",
"%",
"m",
"ft",
"Pa",
"psi",
"#",
]
Classes
class Datatype (*args, **kwargs)
-
Enum representation of the different values, the
$datatype
attribute can takeExpand source code
class Datatype(Enum): "Enum representation of the different values, the `$datatype` attribute can take" INTEGER = auto() FLOAT = auto() BOOLEAN = auto() STRING = auto() ENUM = auto() COLOR = auto() @property def payload(self) -> str: """ Serializes the object conforming it to be used inside a MQTT payload >>> Datatype.INTEGER.payload 'integer' >>> Datatype.FLOAT.payload 'float' """ return self.name.lower() # pylint: disable=no-member
Ancestors
- enum.Enum
Class variables
var BOOLEAN
-
Boolean type.
var COLOR
-
Color type.
var ENUM
-
Enum type.
var FLOAT
-
Float type.
var INTEGER
-
Integer type.
var STRING
-
String type.
class Property (*args, **kwargs)
-
Object representation of a property according to the Homie topology
Expand source code
class Property(NamedTuple): "Object representation of a property according to the Homie topology" name: str get: Callable[[], str] datatype: Datatype = Datatype.STRING set: Optional[Callable[[str], None]] = None retained: Optional[bool] = None settable: Optional[bool] = None unit: Optional[str] = None formatOf: Optional[str] = None def messages(self, prefix: str) -> Iterable[Message]: """ Yields the messages from the property attributes. All its messages are prefixed with its only parameter. >>> prop = Property("A Property!", get=lambda: "hello world") >>> next(prop.messages("/prefix")).payload 'A Property!' """ msg = partial(Message, prefix=prefix) yield msg("$name", self.name) yield msg("$datatype", self.datatype.payload) if self.retained is not None: retained = "true" if self.retained else "false" yield msg("$retained", retained) if self.settable is not None: settable = "true" if self.settable else "false" yield msg("$settable", settable) if self.unit is not None: yield msg("$unit", self.unit) if self.formatOf is not None: yield msg("$format", self.formatOf) def getter_message(self, prefix: str) -> Message: """ Calls the property getter and returns its respective message >>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER) >>> msg = prop.getter_message("homie/device/node/prop") >>> msg.topic 'homie/device/node/prop' >>> msg.payload '4' """ msg = partial(Message, prefix=prefix) retained = True if self.retained is None else self.retained return msg("", self.get(), retained=retained) def setter_message(self, prefix: str, payload: str = "") -> Optional[Message]: """ Creates the necessary message to call the property setter. Returns None if the property is not settable. >>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER, settable=False) >>> prop.setter_message("homie/device/node/prop", "2") is None True >>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER, settable=True) >>> msg = prop.setter_message("homie/device/node/prop", "2") >>> msg.topic 'homie/device/node/prop/set' >>> msg.payload '2' """ if self.settable is not True: return None msg = partial(Message, prefix=prefix) return msg("set", payload)
Ancestors
- builtins.tuple
Instance variables
var datatype
-
The property's datatype.
var formatOf
-
Specifies restrictions or options for the given data type
var get
-
Alias for field number 1
var name
-
Friendly name of the property.
var retained
-
Non-retained (false). The spec Default is Retained (true).
var set
-
Alias for field number 3
var settable
-
Settable (true). Default is read-only (false)
var unit
-
Optional unit of this property. Should follow a accepted value.
Methods
def getter_message(self, prefix: str) -> Message
-
Calls the property getter and returns its respective message
>>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER) >>> msg = prop.getter_message("homie/device/node/prop") >>> msg.topic 'homie/device/node/prop' >>> msg.payload '4'
Expand source code
def getter_message(self, prefix: str) -> Message: """ Calls the property getter and returns its respective message >>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER) >>> msg = prop.getter_message("homie/device/node/prop") >>> msg.topic 'homie/device/node/prop' >>> msg.payload '4' """ msg = partial(Message, prefix=prefix) retained = True if self.retained is None else self.retained return msg("", self.get(), retained=retained)
def messages(self, prefix: str) -> Iterable[Message]
-
Yields the messages from the property attributes. All its messages are prefixed with its only parameter.
>>> prop = Property("A Property!", get=lambda: "hello world") >>> next(prop.messages("/prefix")).payload 'A Property!'
Expand source code
def messages(self, prefix: str) -> Iterable[Message]: """ Yields the messages from the property attributes. All its messages are prefixed with its only parameter. >>> prop = Property("A Property!", get=lambda: "hello world") >>> next(prop.messages("/prefix")).payload 'A Property!' """ msg = partial(Message, prefix=prefix) yield msg("$name", self.name) yield msg("$datatype", self.datatype.payload) if self.retained is not None: retained = "true" if self.retained else "false" yield msg("$retained", retained) if self.settable is not None: settable = "true" if self.settable else "false" yield msg("$settable", settable) if self.unit is not None: yield msg("$unit", self.unit) if self.formatOf is not None: yield msg("$format", self.formatOf)
def setter_message(self, prefix: str, payload: str = '') -> Union[Message, NoneType]
-
Creates the necessary message to call the property setter. Returns None if the property is not settable.
>>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER, settable=False) >>> prop.setter_message("homie/device/node/prop", "2") is None True >>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER, settable=True) >>> msg = prop.setter_message("homie/device/node/prop", "2") >>> msg.topic 'homie/device/node/prop/set' >>> msg.payload '2'
Expand source code
def setter_message(self, prefix: str, payload: str = "") -> Optional[Message]: """ Creates the necessary message to call the property setter. Returns None if the property is not settable. >>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER, settable=False) >>> prop.setter_message("homie/device/node/prop", "2") is None True >>> prop = Property("P", get=lambda: "4", datatype=Datatype.INTEGER, settable=True) >>> msg = prop.setter_message("homie/device/node/prop", "2") >>> msg.topic 'homie/device/node/prop/set' >>> msg.payload '2' """ if self.settable is not True: return None msg = partial(Message, prefix=prefix) return msg("set", payload)