From 92bd5a074ae132b8dae378becc34b668c566b5ec Mon Sep 17 00:00:00 2001 From: TheYOSH Date: Wed, 25 Sep 2024 21:37:43 +0200 Subject: [PATCH] First attempt support Sonoff DIY --- hardware/relay/sonoff_relay.py | 72 +++++++++++++++++++++------------- terrariumUtils.py | 16 +++----- 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/hardware/relay/sonoff_relay.py b/hardware/relay/sonoff_relay.py index 8d2663920..aa72fc746 100644 --- a/hardware/relay/sonoff_relay.py +++ b/hardware/relay/sonoff_relay.py @@ -14,6 +14,12 @@ class terrariumRelaySonoff(terrariumRelay): re.IGNORECASE, ) + # Input format should be either: + # - http://[HOST] + # - http://[HOST]#[POWER_SWITCH_NR] + # - http://[HOST]/#[POWER_SWITCH_NR] + # - http://[PASSWORD]@[HOST]#[POWER_SWITCH_NR] + # - http://[PASSWORD]@[HOST]/#[POWER_SWITCH_NR] @property def _address(self): address = None @@ -30,12 +36,6 @@ def _address(self): return address def _load_hardware(self): - # Input format should be either: - # - http://[HOST]#[POWER_SWITCH_NR] - # - http://[HOST]/#[POWER_SWITCH_NR] - # - http://[PASSWORD]@[HOST]#[POWER_SWITCH_NR] - # - http://[PASSWORD]@[HOST]/#[POWER_SWITCH_NR] - address = self._address # Try Tasmota @@ -100,7 +100,8 @@ def _get_hardware_value(self): class terrariumRelayDimmerSonoffD1(terrariumRelayDimmer): HARDWARE = "sonoff_d1-dimmer" - NAME = "Sonoff D1 Dimmer (Tasmota)" + NAME = "Sonoff D1 Dimmer (Tasmota/DIY)" + MODE = None __URL_REGEX = re.compile( r"^(?Phttps?):\/\/((?P[^:]+):(?P[^@]+)@)?(?P[^#\/]+)(\/)?(#(?P\d+))?$", @@ -123,19 +124,12 @@ def _address(self): return address def _load_hardware(self): - # Input format should be either: - # - http://[HOST]#[POWER_SWITCH_NR] - # - http://[HOST]/#[POWER_SWITCH_NR] - # - http://[PASSWORD]@[HOST]#[POWER_SWITCH_NR] - # - http://[PASSWORD]@[HOST]/#[POWER_SWITCH_NR] - address = self._address # Try Tasmota - # http://sonoff/cm?cmnd=Power[POWER_SWITCH_NR]%201 - # http://sonoff/cm?cmnd=Power[POWER_SWITCH_NR]%200 - # http://sonoff/cm?user=admin&password=joker&cmnd=Power[POWER_SWITCH_NR]%201 + # http://sonoff/cm?cmnd=Dimmer%20[STATE] + # Tasmota test device = f'{address["protocol"]}://{address["host"]}/cm?' if "user" in address and "password" in address: @@ -144,27 +138,51 @@ def _load_hardware(self): device += "cmnd=" state = terrariumUtils.get_remote_data(f"{device}Status%200") - if state is None: - return None + if state is not None: + self.MODE = "tasmota" - return device + return device + + # DIY test + # http://sonoff:8081/zeroconf + # https://sonoff.tech/sonoff-diy-developer-documentation-d1-http-api/#9 + device = f'{address["protocol"]}://{address["host"]}/zeroconf' + state = terrariumUtils.get_remote_data(f"{device}/info", json=True, post={ 'deviceid': '', 'data': { } }) + + if state is not None: + self.MODE = "DIY" + + return device + + return None def _set_hardware_value(self, state): state = int(max(0.0, min(100.0, float(state + self._dimmer_offset)))) - url = f"{self.device}Dimmer%20{state}" - data = terrariumUtils.get_remote_data(url) + if self.MODE == "tasmota": + data = terrariumUtils.get_remote_data(f"{self.device}Dimmer%20{state}") + elif self.MODE == "DIY": + if state == 0: + data = terrariumUtils.get_remote_data(f"{self.device}/switch", json=True, post={ "deviceid": "", "data": { "switch": "off" } }) + else: + # request switch on and specified brightness + # switch must be on, mode 0, brightness between brightmin and brightmax + data = terrariumUtils.get_remote_data(f"{self.device}/switch", json=True, post={ "deviceid": "", "data": { "switch": "on", "brightness": state, "mode": 0, "brightmin": 0, "brightmax": 100 } }) if data is None: return False - return state == int(data["Dimmer"]) + return True def _get_hardware_value(self): - url = f"{self.device}Dimmer" - data = terrariumUtils.get_remote_data(url) - - if data is not None and "Dimmer" in data: - return int(data["Dimmer"]) + if self.MODE == "tasmota": + data = terrariumUtils.get_remote_data(f"{self.device}Dimmer") + if data is not None and "Dimmer" in data: + return int(data["Dimmer"]) + + elif self.MODE == "DIY": + data = terrariumUtils.get_remote_data(f"{self.device}/info", json=True, post={ 'deviceid': '', 'data': { } }) + if data is not None and "data" in data: + return int(data["data"]["brightness"]) return None diff --git a/terrariumUtils.py b/terrariumUtils.py index 8914c9807..5450e5c34 100644 --- a/terrariumUtils.py +++ b/terrariumUtils.py @@ -374,26 +374,20 @@ def parse_time(value): return time @staticmethod - def get_remote_data(url, timeout=3, proxy=None, json=False): + def get_remote_data(url, timeout=3, proxy=None, json=False, post=None): data = None try: url_data = terrariumUtils.parse_url(url) + auth = None if url_data["username"] is None else (url_data["username"], url_data["password"]) proxies = {"http": proxy, "https": proxy} headers = {} if json: headers["Accept"] = "application/json" - if url_data["username"] is None: - response = requests.get(url, headers=headers, timeout=timeout, proxies=proxies, stream=True) + if post is None: + response = requests.get(url, auth=auth, headers=headers, timeout=timeout, proxies=proxies, stream=True) else: - response = requests.get( - url, - auth=(url_data["username"], url_data["password"]), - headers=headers, - timeout=timeout, - proxies=proxies, - stream=True, - ) + response = requests.post(url, auth=auth, headers=headers, timeout=timeout, proxies=proxies, json=post) if response.status_code == 200: if "multipart/x-mixed-replace" in response.headers["content-type"]: