Module lib.server.notifier
Class used to manage a list of notifier, and postpone notification if wifi station not yet connected
Expand source code
# Distributed under Pycameresp License
# Copyright (c) 2023 Remi BERTHOLET
""" Class used to manage a list of notifier, and postpone notification if wifi station not yet connected """
# pylint:disable=consider-using-f-string
# pylint:disable=consider-using-enumerate
import uasyncio
import wifi.wifi
import wifi.station
import server.wanip
import server.httpclient
import tools.logger
import tools.strings
import tools.tasking
import tools.info
import tools.lang
import tools.date
import tools.topic
import tools.filesystem
class Notification:
""" Notification message """
def __init__(self, **kwargs):
""" Notification constructor """
self.topic = kwargs.get("topic", None)
self.value = kwargs.get("value", None)
self.message = kwargs.get("message",None)
self.data = kwargs.get("data", None)
self.forced = kwargs.get("forced", False)
self.display = kwargs.get("display",True)
self.url = kwargs.get("url", None)
self.sent = []
self.retry = 0
class Notifier:
""" Class used to manage a list of notifier, and postpone notification if wifi station not yet connected """
notifiers = []
postponed = []
wake_up_event = None
daily_callback = None
one_per_day = None
daily_notification = [False]
@staticmethod
def init():
""" Initialize """
if Notifier.wake_up_event is None:
Notifier.wake_up_event = uasyncio.Event()
if Notifier.daily_callback is None:
Notifier.daily_callback = Notifier.default_daily_notifier
@staticmethod
def add():
""" Add a callback on subscription """
def add_function(function):
Notifier.notifiers.append(function)
return function
return add_function
@staticmethod
def remove(callback):
""" Remove notifier callback """
for i in range(len(Notifier.notifiers)):
if Notifier.notifiers[i] == callback:
del Notifier.notifiers[i]
break
@staticmethod
def is_empty():
""" Indicates that no notifier registered or not """
if len(Notifier.notifiers) == 0:
return True
return False
@staticmethod
def wake_up():
""" Wake up notifier it to perform tasks """
Notifier.init()
Notifier.wake_up_event.set()
@staticmethod
def to_string(**kwargs):
""" Convert notification into string """
result = b""
item = kwargs.get("message",None)
if item is not None:
result += b"msg='''\n%s''' "%tools.strings.tobytes(item)
item = kwargs.get("topic",None)
if item is not None:
result += b"topic=%s "%tools.strings.tobytes(item)
item = kwargs.get("value",None)
if item is not None:
result += b"value=%s "%tools.strings.tobytes(item)
item = kwargs.get("data",None)
if item is not None:
result += b"len=%d "%len(item)
return tools.strings.tostrings(result)
@staticmethod
def notify(**kwargs):
""" Notify message for all notifier registered
topic : topic name of notification
message : message text
data : binary buffer
forced : true to force the notification, false depend of configuration
display : true to display in syslog, false hide in syslog
url : url link for web hook
enabled : true to enable notification, false to disable"""
display = kwargs.get("display",True)
enabled = kwargs.get("enabled",True)
forced = kwargs.get("forced",False)
message = Notifier.to_string(**kwargs)
tools.logger.syslog("Notification %s %s"%(message, "" if enabled else "not sent"), display=display)
if enabled or forced:
# If postponed message list too long
if len(Notifier.postponed) > 10:
# Remove older
notification = Notifier.postponed[0]
tools.logger.syslog("Notification %s failed to send"%message, display=display)
del Notifier.postponed[0]
# Add message into postponed list
Notifier.postponed.append(Notification(**kwargs))
Notifier.wake_up()
else:
return True
@staticmethod
async def flush():
""" Flush postponed message if wan connected """
# If wan available
if wifi.wifi.Wifi.is_wan_available():
result = True
# Try to send message
for notification in Notifier.postponed:
for notifier in Notifier.notifiers:
if len(Notifier.notifiers) > len(notification.sent):
res = await notifier(notification)
if res is False:
result = False
if notification.retry == 0:
tools.logger.syslog("Cannot send notification")
notification.retry += 1
not_sent = []
for notification in Notifier.postponed:
if len(Notifier.notifiers) > len(notification.sent):
if notification.retry < 32:
not_sent.append(notification)
Notifier.postponed = not_sent
@staticmethod
async def task():
""" Run the task """
Notifier.init()
if Notifier.is_one_per_day() or Notifier.daily_notification[0] is True:
Notifier.daily_notification[0] = False
if Notifier.daily_callback:
# pylint:disable=not-callable
message = Notifier.daily_callback()
Notifier.notify(topic=tools.topic.information, message=message)
# If no notification should be sent
await Notifier.flush()
try:
# Wait notification
await uasyncio.wait_for(Notifier.wake_up_event.wait(), 11)
# Clear event notification event
Notifier.wake_up_event.clear()
except:
pass
@staticmethod
def daily_notify():
""" Force the send of daily notification """
Notifier.daily_notification[0] = True
@staticmethod
def is_one_per_day():
""" Indicates if the action must be done on per day """
current_date = tools.date.date_to_bytes()[:14]
if Notifier.one_per_day is None or (current_date[-2:] == b"12" and current_date != Notifier.one_per_day):
Notifier.one_per_day = current_date
if tools.strings.ticks() > 30000:
return True
return False
@staticmethod
def default_daily_notifier():
""" Return the default message notification """
message = "%s\n"%tools.strings.tostrings(wifi.station.Station.get_hostname())
message += " - Lan Ip : %s\n"%wifi.station.Station.get_info()[0]
message += " - Wan Ip : %s\n"%server.wanip.WanIp.wan_ip
message += " - Uptime : %s\n"%tools.strings.tostrings(tools.info.uptime())
message += " - %s : %s\n"%(tools.strings.tostrings(tools.lang.memory_label), tools.strings.tostrings(tools.info.meminfo()))
message += " - %s : %s\n"%(tools.strings.tostrings(tools.lang.flash_label), tools.strings.tostrings(tools.info.flashinfo()))
return message
@staticmethod
def set_daily_notifier(callback):
""" Replace the daily notification (callback which return a string with message to notify) """
Notifier.daily_callback = callback
@staticmethod
def start():
""" Start notifier task """
tools.tasking.Tasks.create_monitor(Notifier.task)
Classes
class Notification (**kwargs)-
Notification message
Notification constructor
Expand source code
class Notification: """ Notification message """ def __init__(self, **kwargs): """ Notification constructor """ self.topic = kwargs.get("topic", None) self.value = kwargs.get("value", None) self.message = kwargs.get("message",None) self.data = kwargs.get("data", None) self.forced = kwargs.get("forced", False) self.display = kwargs.get("display",True) self.url = kwargs.get("url", None) self.sent = [] self.retry = 0 class Notifier-
Class used to manage a list of notifier, and postpone notification if wifi station not yet connected
Expand source code
class Notifier: """ Class used to manage a list of notifier, and postpone notification if wifi station not yet connected """ notifiers = [] postponed = [] wake_up_event = None daily_callback = None one_per_day = None daily_notification = [False] @staticmethod def init(): """ Initialize """ if Notifier.wake_up_event is None: Notifier.wake_up_event = uasyncio.Event() if Notifier.daily_callback is None: Notifier.daily_callback = Notifier.default_daily_notifier @staticmethod def add(): """ Add a callback on subscription """ def add_function(function): Notifier.notifiers.append(function) return function return add_function @staticmethod def remove(callback): """ Remove notifier callback """ for i in range(len(Notifier.notifiers)): if Notifier.notifiers[i] == callback: del Notifier.notifiers[i] break @staticmethod def is_empty(): """ Indicates that no notifier registered or not """ if len(Notifier.notifiers) == 0: return True return False @staticmethod def wake_up(): """ Wake up notifier it to perform tasks """ Notifier.init() Notifier.wake_up_event.set() @staticmethod def to_string(**kwargs): """ Convert notification into string """ result = b"" item = kwargs.get("message",None) if item is not None: result += b"msg='''\n%s''' "%tools.strings.tobytes(item) item = kwargs.get("topic",None) if item is not None: result += b"topic=%s "%tools.strings.tobytes(item) item = kwargs.get("value",None) if item is not None: result += b"value=%s "%tools.strings.tobytes(item) item = kwargs.get("data",None) if item is not None: result += b"len=%d "%len(item) return tools.strings.tostrings(result) @staticmethod def notify(**kwargs): """ Notify message for all notifier registered topic : topic name of notification message : message text data : binary buffer forced : true to force the notification, false depend of configuration display : true to display in syslog, false hide in syslog url : url link for web hook enabled : true to enable notification, false to disable""" display = kwargs.get("display",True) enabled = kwargs.get("enabled",True) forced = kwargs.get("forced",False) message = Notifier.to_string(**kwargs) tools.logger.syslog("Notification %s %s"%(message, "" if enabled else "not sent"), display=display) if enabled or forced: # If postponed message list too long if len(Notifier.postponed) > 10: # Remove older notification = Notifier.postponed[0] tools.logger.syslog("Notification %s failed to send"%message, display=display) del Notifier.postponed[0] # Add message into postponed list Notifier.postponed.append(Notification(**kwargs)) Notifier.wake_up() else: return True @staticmethod async def flush(): """ Flush postponed message if wan connected """ # If wan available if wifi.wifi.Wifi.is_wan_available(): result = True # Try to send message for notification in Notifier.postponed: for notifier in Notifier.notifiers: if len(Notifier.notifiers) > len(notification.sent): res = await notifier(notification) if res is False: result = False if notification.retry == 0: tools.logger.syslog("Cannot send notification") notification.retry += 1 not_sent = [] for notification in Notifier.postponed: if len(Notifier.notifiers) > len(notification.sent): if notification.retry < 32: not_sent.append(notification) Notifier.postponed = not_sent @staticmethod async def task(): """ Run the task """ Notifier.init() if Notifier.is_one_per_day() or Notifier.daily_notification[0] is True: Notifier.daily_notification[0] = False if Notifier.daily_callback: # pylint:disable=not-callable message = Notifier.daily_callback() Notifier.notify(topic=tools.topic.information, message=message) # If no notification should be sent await Notifier.flush() try: # Wait notification await uasyncio.wait_for(Notifier.wake_up_event.wait(), 11) # Clear event notification event Notifier.wake_up_event.clear() except: pass @staticmethod def daily_notify(): """ Force the send of daily notification """ Notifier.daily_notification[0] = True @staticmethod def is_one_per_day(): """ Indicates if the action must be done on per day """ current_date = tools.date.date_to_bytes()[:14] if Notifier.one_per_day is None or (current_date[-2:] == b"12" and current_date != Notifier.one_per_day): Notifier.one_per_day = current_date if tools.strings.ticks() > 30000: return True return False @staticmethod def default_daily_notifier(): """ Return the default message notification """ message = "%s\n"%tools.strings.tostrings(wifi.station.Station.get_hostname()) message += " - Lan Ip : %s\n"%wifi.station.Station.get_info()[0] message += " - Wan Ip : %s\n"%server.wanip.WanIp.wan_ip message += " - Uptime : %s\n"%tools.strings.tostrings(tools.info.uptime()) message += " - %s : %s\n"%(tools.strings.tostrings(tools.lang.memory_label), tools.strings.tostrings(tools.info.meminfo())) message += " - %s : %s\n"%(tools.strings.tostrings(tools.lang.flash_label), tools.strings.tostrings(tools.info.flashinfo())) return message @staticmethod def set_daily_notifier(callback): """ Replace the daily notification (callback which return a string with message to notify) """ Notifier.daily_callback = callback @staticmethod def start(): """ Start notifier task """ tools.tasking.Tasks.create_monitor(Notifier.task)Class variables
var daily_callbackvar daily_notificationvar notifiersvar one_per_dayvar postponedvar wake_up_event
Static methods
def add()-
Add a callback on subscription
Expand source code
@staticmethod def add(): """ Add a callback on subscription """ def add_function(function): Notifier.notifiers.append(function) return function return add_function def daily_notify()-
Force the send of daily notification
Expand source code
@staticmethod def daily_notify(): """ Force the send of daily notification """ Notifier.daily_notification[0] = True def default_daily_notifier()-
Return the default message notification
Expand source code
@staticmethod def default_daily_notifier(): """ Return the default message notification """ message = "%s\n"%tools.strings.tostrings(wifi.station.Station.get_hostname()) message += " - Lan Ip : %s\n"%wifi.station.Station.get_info()[0] message += " - Wan Ip : %s\n"%server.wanip.WanIp.wan_ip message += " - Uptime : %s\n"%tools.strings.tostrings(tools.info.uptime()) message += " - %s : %s\n"%(tools.strings.tostrings(tools.lang.memory_label), tools.strings.tostrings(tools.info.meminfo())) message += " - %s : %s\n"%(tools.strings.tostrings(tools.lang.flash_label), tools.strings.tostrings(tools.info.flashinfo())) return message async def flush()-
Flush postponed message if wan connected
Expand source code
@staticmethod async def flush(): """ Flush postponed message if wan connected """ # If wan available if wifi.wifi.Wifi.is_wan_available(): result = True # Try to send message for notification in Notifier.postponed: for notifier in Notifier.notifiers: if len(Notifier.notifiers) > len(notification.sent): res = await notifier(notification) if res is False: result = False if notification.retry == 0: tools.logger.syslog("Cannot send notification") notification.retry += 1 not_sent = [] for notification in Notifier.postponed: if len(Notifier.notifiers) > len(notification.sent): if notification.retry < 32: not_sent.append(notification) Notifier.postponed = not_sent def init()-
Initialize
Expand source code
@staticmethod def init(): """ Initialize """ if Notifier.wake_up_event is None: Notifier.wake_up_event = uasyncio.Event() if Notifier.daily_callback is None: Notifier.daily_callback = Notifier.default_daily_notifier def is_empty()-
Indicates that no notifier registered or not
Expand source code
@staticmethod def is_empty(): """ Indicates that no notifier registered or not """ if len(Notifier.notifiers) == 0: return True return False def is_one_per_day()-
Indicates if the action must be done on per day
Expand source code
@staticmethod def is_one_per_day(): """ Indicates if the action must be done on per day """ current_date = tools.date.date_to_bytes()[:14] if Notifier.one_per_day is None or (current_date[-2:] == b"12" and current_date != Notifier.one_per_day): Notifier.one_per_day = current_date if tools.strings.ticks() > 30000: return True return False def notify(**kwargs)-
Notify message for all notifier registered topic : topic name of notification message : message text data : binary buffer forced : true to force the notification, false depend of configuration display : true to display in syslog, false hide in syslog url : url link for web hook enabled : true to enable notification, false to disable
Expand source code
@staticmethod def notify(**kwargs): """ Notify message for all notifier registered topic : topic name of notification message : message text data : binary buffer forced : true to force the notification, false depend of configuration display : true to display in syslog, false hide in syslog url : url link for web hook enabled : true to enable notification, false to disable""" display = kwargs.get("display",True) enabled = kwargs.get("enabled",True) forced = kwargs.get("forced",False) message = Notifier.to_string(**kwargs) tools.logger.syslog("Notification %s %s"%(message, "" if enabled else "not sent"), display=display) if enabled or forced: # If postponed message list too long if len(Notifier.postponed) > 10: # Remove older notification = Notifier.postponed[0] tools.logger.syslog("Notification %s failed to send"%message, display=display) del Notifier.postponed[0] # Add message into postponed list Notifier.postponed.append(Notification(**kwargs)) Notifier.wake_up() else: return True def remove(callback)-
Remove notifier callback
Expand source code
@staticmethod def remove(callback): """ Remove notifier callback """ for i in range(len(Notifier.notifiers)): if Notifier.notifiers[i] == callback: del Notifier.notifiers[i] break def set_daily_notifier(callback)-
Replace the daily notification (callback which return a string with message to notify)
Expand source code
@staticmethod def set_daily_notifier(callback): """ Replace the daily notification (callback which return a string with message to notify) """ Notifier.daily_callback = callback def start()-
Start notifier task
Expand source code
@staticmethod def start(): """ Start notifier task """ tools.tasking.Tasks.create_monitor(Notifier.task) async def task()-
Run the task
Expand source code
@staticmethod async def task(): """ Run the task """ Notifier.init() if Notifier.is_one_per_day() or Notifier.daily_notification[0] is True: Notifier.daily_notification[0] = False if Notifier.daily_callback: # pylint:disable=not-callable message = Notifier.daily_callback() Notifier.notify(topic=tools.topic.information, message=message) # If no notification should be sent await Notifier.flush() try: # Wait notification await uasyncio.wait_for(Notifier.wake_up_event.wait(), 11) # Clear event notification event Notifier.wake_up_event.clear() except: pass def to_string(**kwargs)-
Convert notification into string
Expand source code
@staticmethod def to_string(**kwargs): """ Convert notification into string """ result = b"" item = kwargs.get("message",None) if item is not None: result += b"msg='''\n%s''' "%tools.strings.tobytes(item) item = kwargs.get("topic",None) if item is not None: result += b"topic=%s "%tools.strings.tobytes(item) item = kwargs.get("value",None) if item is not None: result += b"value=%s "%tools.strings.tobytes(item) item = kwargs.get("data",None) if item is not None: result += b"len=%d "%len(item) return tools.strings.tostrings(result) def wake_up()-
Wake up notifier it to perform tasks
Expand source code
@staticmethod def wake_up(): """ Wake up notifier it to perform tasks """ Notifier.init() Notifier.wake_up_event.set()