From 914ad0f623817a0c0ec8cd4c3a3f108398e0b402 Mon Sep 17 00:00:00 2001 From: Andrii Date: Sat, 25 May 2024 01:48:26 -0400 Subject: [PATCH] introduced throttle and receive queue configurations --- README.md | 1 - data/config.yaml | 15 +++++++++++++++ mqtt2kasa/config.py | 39 +++++++++++++++++++++++++++++++++++++++ mqtt2kasa/const.py | 3 +++ mqtt2kasa/kasa_wrapper.py | 14 ++++++++++++-- 5 files changed, 69 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 891dae8..cb8d8a7 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,5 @@ There is a docker image aderesh/mqtt2kasa. Please see `docker-compose.yaml` for ### TODO - Add support for the smart strip, so each 'child' can be controlled independently; -- Expose throttle config instead of hardcoded values; - Improve documentation?!? - Use strict yaml (https://hitchdev.com/strictyaml/) diff --git a/data/config.yaml b/data/config.yaml index af71b7b..47f1701 100644 --- a/data/config.yaml +++ b/data/config.yaml @@ -19,6 +19,15 @@ globals: # kasa will monitor the current state of the device every # poll interval, in seconds. You can override on a per device poll_interval: 11 + # throttle_rate_limit specifices `rate_limit` value of the throttler + # default value is `4`. `0` disables throttle + throttle_rate_limit: 4 + # throttle_period specifies `period` value of the throttler + # default value is `60` seconds + throttle_period: 60 + # receive_queue_size specfies command queue size + # Default value is `4`. `0` makes the queue and infinite queue + receive_queue_size: 4 locations: # coffee maker. To turn it on, use mqtt publish # topic: /coffee_maker/switch payload: on @@ -39,6 +48,12 @@ locations: alias: storage poll_interval: 120 emeter_poll_interval: 600 + # example where throttle parameters are specified + bedroom_lights: + host: 192.168.1.33 + throttle_rate_limit: 10 + throttle_period: 10 # seconds + receive_queue_size: 30 keep_alives: # this is a very optional thing but can be useful. It will monitor a # specific topic to determine if a device should be on or off. The diff --git a/mqtt2kasa/config.py b/mqtt2kasa/config.py index 28aff8b..a157fbf 100755 --- a/mqtt2kasa/config.py +++ b/mqtt2kasa/config.py @@ -117,6 +117,45 @@ def emeter_poll_interval(self, location_name): or const.KASA_DEFAULT_EMETER_POLL_INTERVAL ) + def throttle_rate_limit(self, location_name): + locations = self._get_info().locations + if isinstance(locations, collections.abc.Mapping): + location_attributes = locations.get(location_name, {}) + if "throttle_rate_limit" in location_attributes: + return float(location_attributes["throttle_rate_limit"]) + + cfg_globals = self._get_info().cfg_globals + if "throttle_rate_limit" in cfg_globals: + return float(cfg_globals["throttle_rate_limit"]) + + return float(const.KASA_DEFAULT_THROTTLE_RATE_LIMIT) + + def throttle_period(self, location_name): + locations = self._get_info().locations + if isinstance(locations, collections.abc.Mapping): + location_attributes = locations.get(location_name, {}) + if "throttle_period" in location_attributes: + return float(location_attributes["throttle_period"]) + + cfg_globals = self._get_info().cfg_globals + if "throttle_period" in cfg_globals: + return float(cfg_globals["throttle_period"]) + + return float(const.KASA_DEFAULT_THROTTLE_PERIOD) + + def receive_queue_size(self, location_name): + locations = self._get_info().locations + if isinstance(locations, collections.abc.Mapping): + location_attributes = locations.get(location_name, {}) + if "receive_queue_size" in location_attributes: + return float(location_attributes["receive_queue_size"]) + + cfg_globals = self._get_info().cfg_globals + if "receive_queue_size" in cfg_globals: + return float(cfg_globals["receive_queue_size"]) + + return float(const.KASA_DEFAULT_RECEIVE_QUEUE_SIZE) + @property def locations(self): return self._get_info().locations diff --git a/mqtt2kasa/const.py b/mqtt2kasa/const.py index b1ef9de..af52205 100755 --- a/mqtt2kasa/const.py +++ b/mqtt2kasa/const.py @@ -7,3 +7,6 @@ KASA_DEFAULT_POLL_INTERVAL = 10 # [seconds] KASA_DEFAULT_EMETER_POLL_INTERVAL = 0 # [seconds] 0 == disabled KEEP_ALIVE_DEFAULT_TASK_INTERVAL = 1.5 # [seconds] +KASA_DEFAULT_THROTTLE_RATE_LIMIT = 4 # 0 == disabled +KASA_DEFAULT_THROTTLE_PERIOD = 60 +KASA_DEFAULT_RECEIVE_QUEUE_SIZE = 4 # 0 == queue is disabled \ No newline at end of file diff --git a/mqtt2kasa/kasa_wrapper.py b/mqtt2kasa/kasa_wrapper.py index 8dc10c5..0a4167d 100755 --- a/mqtt2kasa/kasa_wrapper.py +++ b/mqtt2kasa/kasa_wrapper.py @@ -13,6 +13,12 @@ logger = log.getLogger() +class NoThrottler: + async def __aenter__(self): + return None + + async def __aexit__(self, exc_type, exc, tb): + pass class Kasa: STATE_ON = "on" @@ -27,8 +33,12 @@ def __init__(self, name: str, topic: str, config: dict): self.alias = config.get("alias") self.poll_interval = Cfg().poll_interval(name) self.emeter_poll_interval = Cfg().emeter_poll_interval(name) - self.recv_q = asyncio.Queue(maxsize=4) - self.throttler = Throttler(rate_limit=4, period=60) + self.recv_q = asyncio.Queue(maxsize=Cfg().receive_queue_size(name)) + rate_limit = Cfg().throttle_rate_limit(name) + if rate_limit > 0: + self.throttler = Throttler(rate_limit=Cfg().throttle_rate_limit(name), period=Cfg().throttle_period(name)) + else: + self.throttler = NoThrottler() self.curr_state = None self.curr_brightness = None self._device = None