Skip to content

Commit

Permalink
First attempt support Sonoff DIY
Browse files Browse the repository at this point in the history
  • Loading branch information
theyosh committed Sep 25, 2024
1 parent d90ed6b commit 92bd5a0
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 38 deletions.
72 changes: 45 additions & 27 deletions hardware/relay/sonoff_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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"^(?P<protocol>https?):\/\/((?P<user>[^:]+):(?P<passwd>[^@]+)@)?(?P<host>[^#\/]+)(\/)?(#(?P<nr>\d+))?$",
Expand All @@ -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:
Expand All @@ -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
16 changes: 5 additions & 11 deletions terrariumUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]:
Expand Down

0 comments on commit 92bd5a0

Please sign in to comment.