Skip to content

Commit

Permalink
optimized templates, small component improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinkk525 committed Oct 31, 2019
1 parent a10ee44 commit 08a02e9
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 32 deletions.
26 changes: 16 additions & 10 deletions _templates/button_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@
# Created on 2019-09-10

"""
example config:
example config for remoteConfig module or as json in components.py:
{
package: <package_path>
component: Button
constructor_args: {
# mqtt_topic: null #optional, defaults to <mqtt_home>/<device_id>/Buzzer/set
# friendly_name: null # optional, friendly name shown in homeassistant gui with mqtt discovery
# mqtt_topic: null # optional, defaults to <mqtt_home>/<device_id>/Button<_count>/set
# friendly_name: null # optional, friendly name shown in homeassistant gui with mqtt discovery
# discover: true # optional, if false no discovery message for homeassistant will be sent.
}
}
"""

# A button is basically a switch with a single-shot action that deactivates itself afterwards.

__updated__ = "2019-10-20"
__version__ = "0.5"
__updated__ = "2019-10-31"
__version__ = "0.6"

from pysmartnode import config
from pysmartnode.utils.component.button import ComponentButton
Expand All @@ -44,13 +45,18 @@ def __init__(self, mqtt_topic=None, friendly_name=None, discover=True):
global _count
self._count = _count
_count += 1

###
# set the initial state otherwise it will be "None" (unknown) and the first request
# will set it accordingly which in case of a button will always be an activation.
initial_state = False # A button will always be False as it is single-shot,
# unless you have a device with a long single-shot action active during reboot.
# You might be able to poll the current state of a device to set the inital state correctly

# mqtt_topic can be adapted otherwise a default mqtt_topic will be generated if None
super().__init__(COMPONENT_NAME, __version__, mqtt_topic, instance_name=None,
wait_for_lock=False, discover=discover)
self._frn = friendly_name
self._state = False # A button will always be False as it is single-shot,
# unless you have a device with a long single-shot action. Then you might
# be able to poll its current state.
wait_for_lock=False, discover=discover, friendly_name=friendly_name,
initial_state=initial_state)

# If the device needs extra code, launch a new coroutine.

Expand Down
20 changes: 17 additions & 3 deletions _templates/component_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
}
"""

__updated__ = "2019-10-21"
__version__ = "1.5"
__updated__ = "2019-10-31"
__version__ = "1.6"

import uasyncio as asyncio
from pysmartnode import config
Expand Down Expand Up @@ -76,7 +76,12 @@ def __init__(self, my_value, # extend or shrink according to your sensor
self.my_value = my_value

self._frn = friendly_name # will default to unique name in discovery if None
asyncio.get_event_loop().create_task(self._loop())

self._loop_coro = self._loop()
# the component might get removed in which case it should be able to locate and stop
# any running loops it created (otherwise the component will create Exceptions and
# won't be able to be fully removed from RAM)
asyncio.get_event_loop().create_task(self._loop_coro)
gc.collect()

async def _init_network(self):
Expand All @@ -100,6 +105,15 @@ async def _loop(self):
await asyncio.sleep(5)
await _mqtt.publish(self._command_topic[:-4], "ON", qos=1) # publishing to state_topic

async def _remove(self):
"""Will be called if the component gets removed"""
# Cancel any loops/asyncio coroutines started by the component
try:
asyncio.cancel(self._loop_coro)
except Exception:
pass
await super()._remove()

async def _discovery(self):
name = "{!s}{!s}".format(COMPONENT_NAME, self._count)
component_topic = _mqtt.getDeviceTopic(name)
Expand Down
26 changes: 15 additions & 11 deletions _templates/switch_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
package: <package_path>
component: Switch
constructor_args: {
# mqtt_topic: null #optional, defaults to <mqtt_home>/<device_id>/Buzzer/set
# mqtt_topic: null # optional, defaults to <mqtt_home>/<device_id>/Switch<_count>/set
# friendly_name: null # optional, friendly name shown in homeassistant gui with mqtt discovery
# discover: true # optional, if false no discovery message for homeassistant will be sent.
}
}
"""

__updated__ = "2019-10-20"
__version__ = "1.6"
__updated__ = "2019-10-31"
__version__ = "1.7"

from pysmartnode import config
from pysmartnode.utils.component.switch import ComponentSwitch
Expand All @@ -44,17 +45,20 @@ def __init__(self, mqtt_topic=None, friendly_name=None, discover=True):
global _count
self._count = _count
_count += 1
# mqtt_topic can be adapted otherwise a default mqtt_topic will
# be generated if None is passed
super().__init__(COMPONENT_NAME, __version__, mqtt_topic, instance_name=None,
wait_for_lock=True, discover=discover)
self._frn = friendly_name

###
# set the initial state otherwise it will be "None" (unknown).
self._state = False
# Might be that you can read your devices state on startup or know the state because
# set the initial state otherwise it will be "None" (unknown) and the first request
# will set it accordingly.
initial_state = None
# should be False/True if can read your devices state on startup or know the state because
# you initialize a pin in a certain state.
###

# mqtt_topic can be adapted otherwise a default mqtt_topic will be generated if None
super().__init__(COMPONENT_NAME, __version__, mqtt_topic, instance_name=None,
wait_for_lock=True, discover=discover, friendly_name=friendly_name,
initial_state=initial_state)

# If the device needs extra code, launch a new coroutine.

#####################
Expand Down
11 changes: 7 additions & 4 deletions pysmartnode/utils/component/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# Copyright Kevin Köck 2019 Released under the MIT license
# Created on 2019-09-10

__updated__ = "2019-10-20"
__version__ = "0.6"
__updated__ = "2019-10-31"
__version__ = "0.7"

from .switch import ComponentSwitch
from pysmartnode.utils.component import Component
Expand All @@ -22,7 +22,7 @@ class ComponentButton(ComponentSwitch):
"""

def __init__(self, component_name, version, command_topic=None, instance_name=None,
wait_for_lock=False, discover=True):
wait_for_lock=False, discover=True, friendly_name=None, initial_state=False):
"""
:param component_name: name of the component that is subclassing this switch (used for discovery and topics)
:param version: version of the component module. will be logged over mqtt
Expand All @@ -32,9 +32,12 @@ def __init__(self, component_name, version, command_topic=None, instance_name=No
meaning the previous device request has to finish before the new one is started.
Otherwise the new one will get ignored.
With a single-shot action it usually doesn't make sense to wait for the lock.
:param friendly_name: friendly name for homeassistant gui
:param initial_state: the initial state of the button, typically False ("OFF") for Pushbutton
"""
super().__init__(component_name, version, command_topic, instance_name, wait_for_lock,
discover, restore_state=False)
discover, restore_state=False, friendly_name=friendly_name,
initial_state=initial_state)
# discover: boolean, if this component should publish its mqtt discovery.
# This can be used to prevent combined Components from exposing underlying
# hardware components like a power switch
Expand Down
12 changes: 8 additions & 4 deletions pysmartnode/utils/component/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# Copyright Kevin Köck 2019 Released under the MIT license
# Created on 2019-09-10

__updated__ = "2019-10-30"
__version__ = "0.8"
__updated__ = "2019-10-31"
__version__ = "0.9"

from pysmartnode.utils.component import Component
from .definitions import DISCOVERY_SWITCH
Expand All @@ -22,7 +22,8 @@ class ComponentSwitch(Component):
"""

def __init__(self, component_name, version, command_topic=None, instance_name=None,
wait_for_lock=True, discover=True, restore_state=True):
wait_for_lock=True, discover=True, restore_state=True, friendly_name=None,
initial_state=None):
"""
:param component_name: name of the component that is subclassing this switch (used for discovery and topics)
:param version: version of the component module. will be logged over mqtt
Expand All @@ -32,13 +33,15 @@ def __init__(self, component_name, version, command_topic=None, instance_name=No
:param restore_state: restore the retained state topic state
meaning the previous device request has to finish before the new one is started.
Otherwise the new one will get ignored.
:param friendly_name: friendly name for homeassistant gui
:param initial_state: intitial state of the switch. By default unknown so first state change request will set initial state.
"""
super().__init__(component_name, version, discover=discover)
# discover: boolean, if this component should publish its mqtt discovery.
# This can be used to prevent combined Components from exposing underlying
# hardware components like a power switch

self._state = None # initial state is unknown
self._state = initial_state # initial state is unknown if None
self._topic = command_topic or _mqtt.getDeviceTopic(
"{!s}{!s}/set".format(component_name, self._count))
_mqtt.subscribeSync(self._topic, self.on_message, self, check_retained_state=restore_state)
Expand All @@ -48,6 +51,7 @@ def __init__(self, component_name, version, command_topic=None, instance_name=No
self._name = instance_name
self._count = "" # declare in subclass
self._event = None
self._frn = friendly_name
gc.collect()

def getStateChangeEvent(self):
Expand Down

0 comments on commit 08a02e9

Please sign in to comment.