'
info_status = str(condition_dict.get('status') or '')
@@ -292,7 +307,7 @@ def _conditionlabel(self, state, conditionset, i):
and condition_dict.get('updatedbynegate') == 'True')\
else "updated by" if not updatedby_none and compare == "updatedby"\
else "not triggered by" if (not triggeredby_none and compare == "triggeredby"
- and condition_dict.get('triggeredbynegate') == 'True')\
+ and condition_dict.get('triggeredbynegate') == 'True')\
else "triggered by" if not triggeredby_none and compare == "triggeredby"\
else "!=" if (not value_none and compare == "value"
and condition_dict.get('negate') == 'True')\
@@ -372,7 +387,6 @@ def drawgraph(self, filename):
previous_state = ''
previous_conditionset = ''
previousconditionset = ''
- previousstate = ''
previousstate_conditionset = ''
for i, state in enumerate(self.__states):
#self._log_debug('Adding state for webif {}', self.__states[state])
@@ -433,6 +447,12 @@ def drawgraph(self, filename):
actions_enter_or_stay = self.__states[state].get('actions_enter_or_stay') or []
actions_stay = self.__states[state].get('actions_stay') or []
actions_leave = self.__states[state].get('actions_leave') or []
+ action_tooltip_count_enter = 0
+ action_tooltip_count_stay = 0
+ action_tooltip_count_leave = 0
+ action_tooltip_enter = ""
+ action_tooltip_stay = ""
+ action_tooltip_leave = ""
for j, conditionset in enumerate(self.__states[state]['conditionsets']):
if len(actions_enter) > 0 or len(actions_enter_or_stay) > 0:
diff --git a/stateengine/__init__.py b/stateengine/__init__.py
index 1b879799e..bfa0c5c71 100755
--- a/stateengine/__init__.py
+++ b/stateengine/__init__.py
@@ -33,7 +33,6 @@
import os
import copy
from lib.model.smartplugin import *
-from lib.item import Items
from .webif import WebInterface
from datetime import datetime
@@ -48,14 +47,13 @@
class StateEngine(SmartPlugin):
- PLUGIN_VERSION = '2.0.0'
+ PLUGIN_VERSION = '2.1.0'
# Constructor
# noinspection PyUnusedLocal,PyMissingConstructor
def __init__(self, sh):
super().__init__()
StateEngineDefaults.logger = self.logger
- self.itemsApi = Items.get_instance()
self._items = self.abitems = {}
self.mod_http = None
self.__sh = sh
@@ -63,9 +61,9 @@ def __init__(self, sh):
self.__cli = None
self.vis_enabled = self._test_visualization()
if not self.vis_enabled:
- self.logger.warning(f'StateEngine is missing the PyDotPlus package, WebIf visualization is disabled')
+ self.logger.warning(f'StateEngine is missing the PyDotPlus package or GraphViz, WebIf visualization is disabled')
self.init_webinterface(WebInterface)
- self.get_sh().stateengine_plugin_functions = StateEngineFunctions.SeFunctions(self.get_sh(), self.logger)
+ self.__sh.stateengine_plugin_functions = StateEngineFunctions.SeFunctions(self.__sh, self.logger)
try:
log_level = self.get_parameter_value("log_level")
startup_log_level = self.get_parameter_value("startup_log_level")
@@ -100,8 +98,8 @@ def __init__(self, sh):
StateEngineDefaults.plugin_version = self.PLUGIN_VERSION
StateEngineDefaults.write_to_log(self.logger)
- StateEngineCurrent.init(self.get_sh())
- base = self.get_sh().get_basedir()
+ StateEngineCurrent.init(self.__sh)
+ base = self.__sh.get_basedir()
log_directory = SeLogger.manage_logdirectory(base, log_directory, False)
SeLogger.log_directory = log_directory
@@ -109,7 +107,6 @@ def __init__(self, sh):
text = "StateEngine extended logging is active. Logging to '{0}' with log level {1}."
self.logger.info(text.format(log_directory, log_level))
-
if log_maxage > 0:
self.logger.info("StateEngine extended log files will be deleted after {0} days.".format(log_maxage))
cron = ['init', '30 0 * *']
@@ -124,14 +121,6 @@ def __init__(self, sh):
# noinspection PyMethodMayBeStatic
def parse_item(self, item):
item.expand_relativepathes('se_manual_logitem', '', '')
- try:
- item.expand_relativepathes('se_item_*', '', '')
- except Exception:
- pass
- try:
- item.expand_relativepathes('se_status_*', '', '')
- except Exception:
- pass
if self.has_iattr(item.conf, "se_manual_include") or self.has_iattr(item.conf, "se_manual_exclude"):
item._eval = "sh.stateengine_plugin_functions.manual_item_update_eval('" + item.property.path + "', caller, source)"
elif self.has_iattr(item.conf, "se_manual_invert"):
@@ -141,12 +130,12 @@ def parse_item(self, item):
# Initialization of plugin
def run(self):
# Initialize
- StateEngineStructs.global_struct = copy.deepcopy(self.itemsApi.return_struct_definitions())
+ StateEngineStructs.global_struct = copy.deepcopy(self.__sh.items.return_struct_definitions())
self.logger.info("Init StateEngine items")
- for item in self.itemsApi.find_items("se_plugin"):
+ for item in self.__sh.find_items("se_plugin"):
if item.conf["se_plugin"] == "active":
try:
- abitem = StateEngineItem.SeItem(self.get_sh(), item, self)
+ abitem = StateEngineItem.SeItem(self.__sh, item, self)
abitem.ab_alive = True
abitem.update_leave_action(self.__default_instant_leaveaction)
abitem.write_to_log()
@@ -161,9 +150,9 @@ def run(self):
else:
self.logger.info("StateEngine deactivated because no items have been found.")
- self.__cli = StateEngineCliCommands.SeCliCommands(self.get_sh(), self._items, self.logger)
+ self.__cli = StateEngineCliCommands.SeCliCommands(self.__sh, self._items, self.logger)
self.alive = True
- self.get_sh().stateengine_plugin_functions.ab_alive = True
+ self.__sh.stateengine_plugin_functions.ab_alive = True
# Stopping of plugin
def stop(self):
@@ -176,7 +165,7 @@ def stop(self):
self._items[item].remove_all_schedulers()
self.alive = False
- self.get_sh().stateengine_plugin_functions.ab_alive = False
+ self.__sh.stateengine_plugin_functions.ab_alive = False
self.logger.debug("stop method finished")
# Determine if caller/source are contained in changed_by list
@@ -184,7 +173,7 @@ def stop(self):
# source: Source to check
# changed_by: List of callers/source (element format :
{{ item.logger.log_level_as_num }}
-
{% for cond in item.webif_infos.keys() %}{% if not p.itemsApi.return_item(cond) == None %}{% if loop.index > 1 %},{% endif %}{{ p.itemsApi.return_item(cond)._name.split('.')[-1] }}{% endif %}{% endfor %}
+
{% for cond in item.webif_infos.keys() %}{% if not p.get_sh().return_item(cond) == None %}{% if loop.index > 1 %},{% endif %}{{ p.get_sh().return_item(cond)._name.split('.')[-1] }}{% endif %}{% endfor %}
diff --git a/zigbee2mqtt/__init__.py b/zigbee2mqtt/__init__.py
index b8cd20b02..2256f9df9 100755
--- a/zigbee2mqtt/__init__.py
+++ b/zigbee2mqtt/__init__.py
@@ -25,6 +25,7 @@
from datetime import datetime
import json
+from logging import DEBUG
from lib.model.mqttplugin import MqttPlugin
@@ -47,7 +48,7 @@
class Zigbee2Mqtt(MqttPlugin):
""" Main class of the Plugin. Does all plugin specific stuff and provides the update functions for the items """
- PLUGIN_VERSION = '2.0.1'
+ PLUGIN_VERSION = '2.0.2'
def __init__(self, sh, **kwargs):
""" Initializes the plugin. """
@@ -55,6 +56,8 @@ def __init__(self, sh, **kwargs):
# Call init code of parent class (MqttPlugin)
super().__init__()
+ # self.logger = logging.getLogger(__name__)
+
self.logger.info(f'Init {self.get_shortname()} plugin {self.PLUGIN_VERSION}')
# get the parameters for the plugin (as defined in metadata plugin.yaml):
@@ -62,8 +65,9 @@ def __init__(self, sh, **kwargs):
self.cycle = self.get_parameter_value('poll_period')
self.read_at_init = self.get_parameter_value('read_at_init')
self._z2m_gui = self.get_parameter_value('z2m_gui')
+ self._pause_item_path = self.get_parameter_value('pause_item')
- # bool_values is only good if used internally, because MQTT data is
+ # bool_values is only good if used internally, because MQTT data is
# usually sent in JSON. So just make this easy...
self.bool_values = [False, True]
@@ -115,6 +119,8 @@ def run(self):
self.logger.debug("Run method called")
self.alive = True
+ if self._pause_item:
+ self._pause_item(False, self.get_fullname())
# start subscription to all topics
self.start_subscriptions()
@@ -134,8 +140,10 @@ def stop(self):
""" Stop method for the plugin """
self.alive = False
+ if self._pause_item:
+ self._pause_item(True, self.get_fullname())
self.logger.debug("Stop method called")
- self.scheduler_remove('z2m_c')
+ self.scheduler_remove('z2m_cycle')
# stop subscription to all topics
self.stop_subscriptions()
@@ -154,15 +162,12 @@ def parse_item(self, item):
can be sent to the knx with a knx write function within the knx plugin.
"""
- # remove this block when its included in smartplugin.py,
- # replace with super().parse_item(item)
- # check for suspend item
- if item.property.path == self._suspend_item_path:
- self.logger.debug(f'suspend item {item.property.path} registered')
- self._suspend_item = item
+ # check for pause item
+ if item.property.path == self._pause_item_path:
+ self.logger.debug(f'pause item {item.property.path} registered')
+ self._pause_item = item
self.add_item(item, updating=True)
return self.update_item
- # end block
if self.has_iattr(item.conf, Z2M_ATTR):
self.logger.debug(f"parsing item: {item}")
@@ -174,13 +179,6 @@ def parse_item(self, item):
attr = self.get_iattr_value(item.conf, Z2M_ATTR)
- if item.type() == 'bool':
- bval = self.get_iattr_value(item.conf, Z2M_BVAL)
- if bval == []:
- bval = None
- if bval is None or type(bval) is not list:
- bval = self.bool_values
-
# invert read-only/write-only logic to allow read/write
write = not self.get_iattr_value(item.conf, Z2M_RO, False)
read = not self.get_iattr_value(item.conf, Z2M_WO, False) or not write
@@ -198,6 +196,9 @@ def parse_item(self, item):
'write': write,
}
if item.type() == 'bool':
+ bval = self.get_iattr_value(item.conf, Z2M_BVAL)
+ if bval is None or bval == [] or type(bval) is not list:
+ bval = self.bool_values
data['bool_values'] = bval
self._devices[device][attr].update(data)
@@ -216,7 +217,7 @@ def parse_item(self, item):
def remove_item(self, item):
if item not in self._plg_item_dict:
- return
+ return False
mapping = self.get_item_mapping(item)
if mapping:
@@ -239,9 +240,9 @@ def remove_item(self, item):
except ValueError:
pass
- super().remove_item(item)
+ return super().remove_item(item)
- def update_item(self, item, caller='', source=None, dest=None):
+ def update_item(self, item, caller=None, source=None, dest=None):
"""
Item has been updated
@@ -252,7 +253,17 @@ def update_item(self, item, caller='', source=None, dest=None):
"""
self.logger.debug(f"update_item: {item} called by {caller} and source {source}")
- if self.alive and not self.suspended and not caller.startswith(self.get_shortname()):
+ # check for pause item
+ if item is self._pause_item:
+ if caller != self.get_shortname():
+ self.logger.debug(f'pause item changed to {item()}')
+ if item() and self.alive:
+ self.stop()
+ elif not item() and not self.alive:
+ self.run()
+ return
+
+ if self.alive and caller and not caller.startswith(self.get_shortname()):
if item in self._items_write:
@@ -311,7 +322,7 @@ def update_item(self, item, caller='', source=None, dest=None):
attr: value
})
else:
- payload = None
+ payload = ''
self.publish_z2m_topic(device, topic_3, topic_4, topic_5, payload, item, bool_values=bool_values)
else:
@@ -417,7 +428,13 @@ def on_mqtt_msg(self, topic: str, payload, qos=None, retain=None):
if item is not None:
item(value, src)
- self.logger.info(f"{device}: Item '{item}' set to value {value}")
+ if device == 'bridge' and (isinstance(value, list) or isinstance(value, dict)):
+ if self.logger.isEnabledFor(DEBUG):
+ self.logger.debug(f"{device}: Item '{item}' set to value {value}")
+ else:
+ self.logger.info(f"{device}: Item '{item}' set to value {str(value)[:80]}[...] (enable debug log for full output)")
+ else:
+ self.logger.info(f"{device}: Item '{item}' set to value {value}")
else:
self.logger.info(f"{device}: No item for attribute '{attr}' defined to set to {value}")
diff --git a/zigbee2mqtt/plugin.yaml b/zigbee2mqtt/plugin.yaml
index d9de7f569..3f9d56658 100755
--- a/zigbee2mqtt/plugin.yaml
+++ b/zigbee2mqtt/plugin.yaml
@@ -12,8 +12,8 @@ plugin:
documentation: ''
support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1856775-support-thread-f%C3%BCr-das-zigbee2mqtt-plugin
- version: 2.0.1 # Plugin version
- sh_minversion: '1.9.5.6' # minimum shNG version to use this plugin
+ version: 2.0.2 # Plugin version
+ sh_minversion: '1.10.0' # minimum shNG version to use this plugin
# sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest)
py_minversion: '3.8' # minimum Python version to use for this plugin
multi_instance: true # plugin supports multi instance
@@ -45,12 +45,12 @@ parameters:
de: Einlesen aller Werte beim Start
en: Read all values at init
- suspend_item:
+ pause_item:
type: str
default: ''
description:
- de: Pfad zum Suspend-Item
- en: Path to suspend item
+ de: Pfad zum Pause-Item
+ en: Path to pause item
z2m_gui:
type: str