Skip to content

Commit

Permalink
Support Sonoff in DIY mode
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexxIT committed Jan 18, 2020
1 parent 2a64735 commit bd37e26
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 23 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [0.2.4] - 2020-01-18

### Added

- Support Sonoff in DIY mode

## [0.2.3] - 2020-01-18

### Added
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- работает с оригинальной прошивкой Sonoff, нет необходимости перепрошивать
устройства
- работает по локальной сети, нет тормозов китайских серверов
- работает с устройствами без DIY-режима
- работает с устройствами в DIY-режиме
- можно получить список устройств с серверов eWeLink, либо настроить его
вручную (список сохраняется локально и может больше не запрашиваться)
- мгновенное получение нового состояния устройств по Multicast (привет
Expand Down Expand Up @@ -86,6 +88,12 @@ sonoff:
channels: [3, 4]
```

Для устройств в режиме DIY хватит:

```yaml
sonoff:
```

## Sonoff RF Bridge 433

Хоть компонент и поддерживает обучение - рекомендуется обучать кнопки через
Expand All @@ -94,7 +102,7 @@ sonoff:
Компонент умеет как отправлять RF-сигналы, так и получать их, но только ранее обученные.

При получении команды создаётся событие `sonoff.remote` с порядковым номером
кнопки и временем срабатывание (в UTC, присылает устройство).
кнопки и временем срабатывания (в UTC, присылает устройство).

`command` - порядковый номер изученной кнопки в приложении.

Expand Down
38 changes: 18 additions & 20 deletions custom_components/sonoff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def setup(hass, hass_config):

# load devices from configuration.yaml
if not devices:
devices = config.get(CONF_DEVICES)
devices = config.get(CONF_DEVICES, {})

# concat ewelink devices with yaml devices
elif CONF_DEVICES in config:
Expand All @@ -53,15 +53,6 @@ def setup(hass, hass_config):
_LOGGER.debug(f"Add device config {deviceid}")
devices[deviceid] = devicecfg

if not devices:
_LOGGER.error("Empty device list")
return False

# add deviceid to all device config
for k, v in devices.items():
if 'deviceid' not in v:
v['deviceid'] = k

hass.data[DOMAIN] = devices

def add_device(devicecfg: dict, state: dict):
Expand Down Expand Up @@ -146,7 +137,6 @@ def add_service(self, zeroconf: Zeroconf, type_: str, name: str):
host = str(ipaddress.ip_address(info.address))
deviceid = properties['id']

# TODO: check no did
device = self.devices.get(deviceid)
if isinstance(device, EWeLinkDevice):
# TODO: check update host
Expand All @@ -166,10 +156,15 @@ def add_service(self, zeroconf: Zeroconf, type_: str, name: str):
# Fix Sonoff RF Bridge sintax bug
if data.startswith(b'{"rf'):
data = data.replace(b'"="', b'":"')
state = json.loads(data)
_LOGGER.debug(f"State: {state}")
else:
raise NotImplementedError()
data = ''.join([properties[f'data{i}'] for i in range(1, 4, 1)
if f'data{i}' in properties])

state = json.loads(data)
_LOGGER.debug(f"State: {state}")

if 'deviceid' not in config:
config['deviceid'] = deviceid

self.devices[deviceid] = EWeLinkDevice(host, config, state, zeroconf)

Expand Down Expand Up @@ -247,12 +242,12 @@ def update_service(self, zeroconf: Zeroconf, type_: str, name: str):
# Fix Sonoff RF Bridge sintax bug
if data.startswith(b'{"rf'):
data = data.replace(b'"="', b'":"')
data = json.loads(data)
_LOGGER.debug(f"Data: {data}")
else:
raise NotImplementedError()
data = ''.join([properties[f'data{i}'] for i in range(1, 4, 1)
if f'data{i}' in properties])

self.state = data
self.state = json.loads(data)
_LOGGER.debug(f"State: {self.state}")

for handler in self._update_handlers:
handler(self)
Expand All @@ -264,12 +259,15 @@ def send(self, command: str, data: dict):
:param data: Данные для команды
:return:
"""
payload = utils.encrypt({
payload = {
'sequence': str(int(time.time())),
'deviceid': self.deviceid,
'selfApikey': '123',
'data': data
}, self.devicekey)
}

if self.devicekey:
payload = utils.encrypt(payload, self.devicekey)

_LOGGER.debug(f"Send {command} to {self.deviceid}: {payload}")

Expand Down
5 changes: 3 additions & 2 deletions custom_components/sonoff/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from typing import Optional

import requests

from Crypto.Cipher import AES
from Crypto.Hash import MD5
from Crypto.Random import get_random_bytes
Expand Down Expand Up @@ -100,8 +101,8 @@ def decrypt(payload: dict, devicekey: str):
hash_.update(devicekey)
key = hash_.digest()

encoded = ''.join([payload[f'data{i}'] for i in range(1, 4, 1) if
f'data{i}' in payload])
encoded = ''.join([payload[f'data{i}'] for i in range(1, 4, 1)
if f'data{i}' in payload])

cipher = AES.new(key, AES.MODE_CBC, iv=b64decode(payload['iv']))
ciphertext = b64decode(encoded)
Expand Down

0 comments on commit bd37e26

Please sign in to comment.