Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial support for Tornado 16X SQ air conditioner (0x4e2a) #430

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4fd2b16
Add GH Actions
KTibow Nov 5, 2020
ebaac1f
Fix branches
KTibow Nov 5, 2020
97077d3
Conditionals
KTibow Nov 5, 2020
25856e3
Update flake8.yaml
KTibow Nov 5, 2020
d09f501
Go back
KTibow Nov 5, 2020
9482804
Update flake8.yaml
KTibow Nov 5, 2020
153b96f
Update flake8.yaml
KTibow Nov 5, 2020
922eb45
Run black
KTibow Nov 5, 2020
75e483c
Clean up get_energy() (#471)
felipediel Nov 7, 2020
1799a8c
Merge branch 'master' into patch-2
mjg59 Nov 7, 2020
29345a1
Merge pull request #470 from KTibow/patch-2
mjg59 Nov 7, 2020
5fcea48
Rebase from main.
enosh Sep 22, 2020
e16fe18
Simplify `set_advanced` to just one request and create `set_partial` …
enosh Nov 28, 2020
cfd2942
Fix modes heating,drying,fan when unit is powered off, in `set_partial`.
enosh Nov 28, 2020
ee3bb1f
Fix some bugs in set_advanced.
enosh Nov 29, 2020
9913097
Better ifs. Fix flake8 issues.
enosh Nov 29, 2020
e245964
Rename `torando` class to `xsq`.
enosh Nov 29, 2020
09527ae
Round target_temp.
enosh Nov 29, 2020
e999a79
Rename class to `sq1`, add clean and mildew support, use f-strings, _…
enosh Dec 1, 2020
8f8c113
Handle checksums correctly!
enosh Dec 1, 2020
692986e
Remove set_partial, integate to set_state (renamed from set_advanced)…
enosh Dec 9, 2020
db50704
Implement an encode func.
enosh Dec 9, 2020
e6d151a
replace some complicated ifs with {}.get
enosh Dec 9, 2020
573de73
avoid struct in encode, more dict use instead of ifs
enosh Dec 9, 2020
e459581
typos, flake8 issues
enosh Dec 9, 2020
d0899e7
First attempt at IntEmun for mode, speed and swing. Simplify _encode.
enosh Dec 10, 2020
85ef56d
Replace a bunch of type checks with creating a new instance.
enosh Dec 10, 2020
bb13719
Move packet creation to a new _send_command and to _enocde.
enosh Dec 11, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/flake8.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Python flake8

on:
push:
branches: [ main, master, dev, development ]
pull_request:
branches: [ main, master, dev, development ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 wemake-python-styleguide
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. ignore magic numbers and use double quotes and ignore numbers with zeroes before them.
# and ignore lowercase hex numbers and ignore isort incorrect imports
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=90 --ignore=WPS432,WPS339,WPS341,I --inline-quotes double --statistics
151 changes: 73 additions & 78 deletions broadlink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import Generator, List, Union, Tuple

from .alarm import S1C
from .climate import hysen
from .climate import hysen, sq1
from .cover import dooya
from .device import device, scan
from .exceptions import exception
Expand All @@ -20,88 +20,88 @@
0x2716: (sp2, "NEO PRO", "Ankuoo"),
0x2717: (sp2, "NEO", "Ankuoo"),
0x2719: (sp2, "SP2-compatible", "Honeywell"),
0x271a: (sp2, "SP2-compatible", "Honeywell"),
0x271A: (sp2, "SP2-compatible", "Honeywell"),
0x2720: (sp2, "SP mini", "Broadlink"),
0x2728: (sp2, "SP2-compatible", "URANT"),
0x2733: (sp2, "SP3", "Broadlink"),
0x2736: (sp2, "SP mini+", "Broadlink"),
0x273e: (sp2, "SP mini", "Broadlink"),
0x273E: (sp2, "SP mini", "Broadlink"),
0x7530: (sp2, "SP2", "Broadlink (OEM)"),
0x7539: (sp2, "SP2-IL", "Broadlink (OEM)"),
0x753e: (sp2, "SP mini 3", "Broadlink"),
0x753E: (sp2, "SP mini 3", "Broadlink"),
0x7540: (sp2, "MP2", "Broadlink"),
0X7544: (sp2, "SP2-CL", "Broadlink"),
0x7544: (sp2, "SP2-CL", "Broadlink"),
0x7546: (sp2, "SP2-UK/BR/IN", "Broadlink (OEM)"),
0x7547: (sp2, "SC1", "Broadlink"),
0x7918: (sp2, "SP2", "Broadlink (OEM)"),
0x7919: (sp2, "SP2-compatible", "Honeywell"),
0x791a: (sp2, "SP2-compatible", "Honeywell"),
0x7d00: (sp2, "SP3-EU", "Broadlink (OEM)"),
0x7d0d: (sp2, "SP mini 3", "Broadlink (OEM)"),
0x791A: (sp2, "SP2-compatible", "Honeywell"),
0x7D00: (sp2, "SP3-EU", "Broadlink (OEM)"),
0x7D0D: (sp2, "SP mini 3", "Broadlink (OEM)"),
0x9479: (sp2, "SP3S-US", "Broadlink"),
0x947a: (sp2, "SP3S-EU", "Broadlink"),
0x756c: (sp4, "SP4M", "Broadlink"),
0x947A: (sp2, "SP3S-EU", "Broadlink"),
0x756C: (sp4, "SP4M", "Broadlink"),
0x7579: (sp4, "SP4L-EU", "Broadlink"),
0x7583: (sp4, "SP mini 3", "Broadlink"),
0x7d11: (sp4, "SP mini 3", "Broadlink"),
0x648b: (sp4b, "SP4M-US", "Broadlink"),
0x7D11: (sp4, "SP mini 3", "Broadlink"),
0x648B: (sp4b, "SP4M-US", "Broadlink"),
0x2712: (rm, "RM pro/pro+", "Broadlink"),
0x272a: (rm, "RM pro", "Broadlink"),
0x272A: (rm, "RM pro", "Broadlink"),
0x2737: (rm, "RM mini 3", "Broadlink"),
0x273d: (rm, "RM pro", "Broadlink"),
0x277c: (rm, "RM home", "Broadlink"),
0x273D: (rm, "RM pro", "Broadlink"),
0x277C: (rm, "RM home", "Broadlink"),
0x2783: (rm, "RM home", "Broadlink"),
0x2787: (rm, "RM pro", "Broadlink"),
0x278b: (rm, "RM plus", "Broadlink"),
0x278f: (rm, "RM mini", "Broadlink"),
0x278B: (rm, "RM plus", "Broadlink"),
0x278F: (rm, "RM mini", "Broadlink"),
0x2797: (rm, "RM pro+", "Broadlink"),
0x279d: (rm, "RM pro+", "Broadlink"),
0x27a1: (rm, "RM plus", "Broadlink"),
0x27a6: (rm, "RM plus", "Broadlink"),
0x27a9: (rm, "RM pro+", "Broadlink"),
0x27c2: (rm, "RM mini 3", "Broadlink"),
0x27c3: (rm, "RM pro+", "Broadlink"),
0x27c7: (rm, "RM mini 3", "Broadlink"),
0x27cc: (rm, "RM mini 3", "Broadlink"),
0x27cd: (rm, "RM mini 3", "Broadlink"),
0x27d0: (rm, "RM mini 3", "Broadlink"),
0x27d1: (rm, "RM mini 3", "Broadlink"),
0x27de: (rm, "RM mini 3", "Broadlink"),
0x51da: (rm4, "RM4 mini", "Broadlink"),
0x5f36: (rm4, "RM mini 3", "Broadlink"),
0x279D: (rm, "RM pro+", "Broadlink"),
0x27A1: (rm, "RM plus", "Broadlink"),
0x27A6: (rm, "RM plus", "Broadlink"),
0x27A9: (rm, "RM pro+", "Broadlink"),
0x27C2: (rm, "RM mini 3", "Broadlink"),
0x27C3: (rm, "RM pro+", "Broadlink"),
0x27C7: (rm, "RM mini 3", "Broadlink"),
0x27CC: (rm, "RM mini 3", "Broadlink"),
0x27CD: (rm, "RM mini 3", "Broadlink"),
0x27D0: (rm, "RM mini 3", "Broadlink"),
0x27D1: (rm, "RM mini 3", "Broadlink"),
0x27DE: (rm, "RM mini 3", "Broadlink"),
0x51DA: (rm4, "RM4 mini", "Broadlink"),
0x5F36: (rm4, "RM mini 3", "Broadlink"),
0x6026: (rm4, "RM4 pro", "Broadlink"),
0x6070: (rm4, "RM4C mini", "Broadlink"),
0x610e: (rm4, "RM4 mini", "Broadlink"),
0x610f: (rm4, "RM4C mini", "Broadlink"),
0x61a2: (rm4, "RM4 pro", "Broadlink"),
0x62bc: (rm4, "RM4 mini", "Broadlink"),
0x62be: (rm4, "RM4C mini", "Broadlink"),
0x648d: (rm4, "RM4 mini", "Broadlink"),
0x649b: (rm4, "RM4 pro", "Broadlink"),
0x653a: (rm4, "RM4 mini", "Broadlink"),
0x610E: (rm4, "RM4 mini", "Broadlink"),
0x610F: (rm4, "RM4C mini", "Broadlink"),
0x61A2: (rm4, "RM4 pro", "Broadlink"),
0x62BC: (rm4, "RM4 mini", "Broadlink"),
0x62BE: (rm4, "RM4C mini", "Broadlink"),
0x648D: (rm4, "RM4 mini", "Broadlink"),
0x649B: (rm4, "RM4 pro", "Broadlink"),
0x653A: (rm4, "RM4 mini", "Broadlink"),
0x2714: (a1, "e-Sensor", "Broadlink"),
0x4eb5: (mp1, "MP1-1K4S", "Broadlink"),
0x4ef7: (mp1, "MP1-1K4S", "Broadlink (OEM)"),
0x4f1b: (mp1, "MP1-1K3S2U", "Broadlink (OEM)"),
0x4f65: (mp1, "MP1-1K3S2U", "Broadlink"),
0x4EB5: (mp1, "MP1-1K4S", "Broadlink"),
0x4EF7: (mp1, "MP1-1K4S", "Broadlink (OEM)"),
0x4F1B: (mp1, "MP1-1K3S2U", "Broadlink (OEM)"),
0x4F65: (mp1, "MP1-1K3S2U", "Broadlink"),
0x5043: (lb1, "SB800TD", "Broadlink (OEM)"),
0x504e: (lb1, "LB1", "Broadlink"),
0x60c7: (lb1, "LB1", "Broadlink"),
0x60c8: (lb1, "LB1", "Broadlink"),
0x504E: (lb1, "LB1", "Broadlink"),
0x60C7: (lb1, "LB1", "Broadlink"),
0x60C8: (lb1, "LB1", "Broadlink"),
0x6112: (lb1, "LB1", "Broadlink"),
0x2722: (S1C, "S2KIT", "Broadlink"),
0x4ead: (hysen, "HY02B05H", "Hysen"),
0x4e4d: (dooya, "DT360E-45/20", "Dooya"),
0x51e3: (bg1, "BG800/BG900", "BG Electrical"),
0X4E2A: (sq1, "SQ", "Tornado"),
0x4EAD: (hysen, "HY02B05H", "Hysen"),
0x4E4D: (dooya, "DT360E-45/20", "Dooya"),
0x51E3: (bg1, "BG800/BG900", "BG Electrical"),
}


def gendevice(
dev_type: int,
host: Tuple[str, int],
mac: Union[bytes, str],
name: str = None,
is_locked: bool = None,
dev_type: int,
host: Tuple[str, int],
mac: Union[bytes, str],
name: str = None,
is_locked: bool = None,
) -> device:
"""Generate a device."""
try:
Expand All @@ -122,10 +122,10 @@ def gendevice(


def hello(
host: str,
port: int = 80,
timeout: int = 10,
local_ip_address: str = None,
host: str,
port: int = 80,
timeout: int = 10,
local_ip_address: str = None,
) -> device:
"""Direct device discovery.

Expand All @@ -138,31 +138,27 @@ def hello(


def discover(
timeout: int = 10,
local_ip_address: str = None,
discover_ip_address: str = '255.255.255.255',
discover_ip_port: int = 80,
timeout: int = 10,
local_ip_address: str = None,
discover_ip_address: str = "255.255.255.255",
discover_ip_port: int = 80,
) -> List[device]:
"""Discover devices connected to the local network."""
responses = scan(
timeout, local_ip_address, discover_ip_address, discover_ip_port
)
responses = scan(timeout, local_ip_address, discover_ip_address, discover_ip_port)
return [gendevice(*resp) for resp in responses]


def xdiscover(
timeout: int = 10,
local_ip_address: str = None,
discover_ip_address: str = '255.255.255.255',
discover_ip_port: int = 80,
timeout: int = 10,
local_ip_address: str = None,
discover_ip_address: str = "255.255.255.255",
discover_ip_port: int = 80,
) -> Generator[device, None, None]:
"""Discover devices connected to the local network.

This function returns a generator that yields devices instantly.
"""
responses = scan(
timeout, local_ip_address, discover_ip_address, discover_ip_port
)
responses = scan(timeout, local_ip_address, discover_ip_address, discover_ip_port)
for resp in responses:
yield gendevice(*resp)

Expand Down Expand Up @@ -191,13 +187,12 @@ def setup(ssid: str, password: str, security_mode: int) -> None:
payload[0x85] = pass_length # Character length of password
payload[0x86] = security_mode # Type of encryption

checksum = sum(payload, 0xbeaf) & 0xffff
payload[0x20] = checksum & 0xff # Checksum 1 position
checksum = sum(payload, 0xBEAF) & 0xFFFF
payload[0x20] = checksum & 0xFF # Checksum 1 position
payload[0x21] = checksum >> 8 # Checksum 2 position

sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Internet # UDP
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.sendto(payload, ('255.255.255.255', 80))
sock.sendto(payload, ("255.255.255.255", 80))
sock.close()
28 changes: 14 additions & 14 deletions broadlink/alarm.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,42 @@ class S1C(device):
"""Controls a Broadlink S1C."""

_SENSORS_TYPES = {
0x31: 'Door Sensor', # 49 as hex
0x91: 'Key Fob', # 145 as hex, as serial on fob corpse
0x21: 'Motion Sensor' # 33 as hex
0x31: "Door Sensor", # 49 as hex
0x91: "Key Fob", # 145 as hex, as serial on fob corpse
0x21: "Motion Sensor", # 33 as hex
}

def __init__(self, *args, **kwargs) -> None:
"""Initialize the controller."""
device.__init__(self, *args, **kwargs)
self.type = 'S1C'
self.type = "S1C"

def get_sensors_status(self) -> dict:
"""Return the state of the sensors."""
packet = bytearray(16)
packet[0] = 0x06 # 0x06 - get sensors info, 0x07 - probably add sensors
response = self.send_packet(0x6a, packet)
response = self.send_packet(0x6A, packet)
check_error(response[0x22:0x24])
payload = self.decrypt(response[0x38:])
if not payload:
return None
count = payload[0x4]
sensor_data = payload[0x6:]
sensors = [
bytearray(sensor_data[i * 83:(i + 1) * 83])
bytearray(sensor_data[i * 83 : (i + 1) * 83])
for i in range(len(sensor_data) // 83)
]
return {
'count': count,
'sensors': [
"count": count,
"sensors": [
{
'status': sensor[0],
'name': sensor[4:26].decode().strip('\x00'),
'type': self._SENSORS_TYPES.get(sensor[3], 'Unknown'),
'order': sensor[1],
'serial': sensor[26:30].hex(),
"status": sensor[0],
"name": sensor[4:26].decode().strip("\x00"),
"type": self._SENSORS_TYPES.get(sensor[3], "Unknown"),
"order": sensor[1],
"serial": sensor[26:30].hex(),
}
for sensor in sensors
if any(sensor[26:30])
]
],
}
Loading