Skip to content
This repository was archived by the owner on Oct 1, 2021. It is now read-only.

Commit d43bec3

Browse files
sfamballoob
authored andcommitted
Add support for Samsung Smart Air Conditioner (#61)
1 parent 2abd925 commit d43bec3

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""Discover Samsung Smart AC devices."""
2+
from . import BaseDiscoverable
3+
4+
5+
class Discoverable(BaseDiscoverable):
6+
"""Add support for discovering a Samsung Smart AC device."""
7+
8+
def __init__(self, netdis):
9+
"""Initialize the Samsung Smart AC discovery."""
10+
self._netdis = netdis
11+
12+
def get_entries(self):
13+
"""Get all the Samsung Smart AC details."""
14+
return self._netdis.samsungac.entries

netdisco/discovery.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .lms import LMS
1111
from .tellstick import Tellstick
1212
from .daikin import Daikin
13+
from .samsungac import SamsungAC
1314

1415
_LOGGER = logging.getLogger(__name__)
1516

@@ -40,6 +41,7 @@ def __init__(self, limit_discovery=None):
4041
self.lms = LMS()
4142
self.tellstick = Tellstick()
4243
self.daikin = Daikin()
44+
self.samsungac = SamsungAC()
4345
self.discoverables = {}
4446

4547
self._load_device_support()
@@ -57,6 +59,7 @@ def scan(self):
5759
self.lms.scan()
5860
self.tellstick.scan()
5961
self.daikin.scan()
62+
self.samsungac.scan()
6063

6164
def stop(self):
6265
"""Turn discovery off."""

netdisco/samsungac.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"""
2+
Support for discovery Samsung Smart Air Conditioner.
3+
4+
"""
5+
import threading
6+
import socket
7+
import binascii
8+
9+
DISCOVERY_ADDRESS = '<broadcast>'
10+
DISCOVERY_PORT = 1900
11+
DISCOVERY_TIMEOUT = 5
12+
13+
DISCOVERY_MSG = """NOTIFY * HTTP/1.1
14+
HOST: 239.255.255.250:1900
15+
CACHE-CONTROL: max-age=20
16+
SERVER: AIR CONDITIONER
17+
SPEC_VER: MSpec-1.00
18+
SERVICE_NAME: ControlServer-MLib
19+
MESSAGE_TYPE: CONTROLLER_START
20+
21+
""".encode('utf-8')
22+
23+
24+
class SamsungAC(object):
25+
"""Base class to discover Samsung Smart AC devices."""
26+
27+
def __init__(self):
28+
self.entries = []
29+
self.last_scan = None
30+
self._lock = threading.RLock()
31+
32+
def scan(self):
33+
"""Scan the network."""
34+
with self._lock:
35+
self.update()
36+
37+
def all(self):
38+
"""Return all found entries.
39+
40+
Will scan for entries if not scanned recently.
41+
"""
42+
self.scan()
43+
return list(self.entries)
44+
45+
def update(self):
46+
"""Scan for new Samsung Smart AC devices.
47+
48+
Example of the dict list returned by this function:
49+
[{'data': {
50+
'CACHE_CONTROL': 'max-age=60',
51+
'FIRMCODE': '01538A140403',
52+
'GROUP_ADDRESS': 'BC8CCDEE1A32FFFF',
53+
'HOST': '255.255.255.255:1900',
54+
'LOCATION': 'http://192.168.1.101',
55+
'MAC_ADDR': 'BC8CCDEE1A32',
56+
'MESSAGE_TYPE': 'DEVICEDESCRIPTION',
57+
'MODELCODE': 'SAMSUNG_DEVICE',
58+
'NICKNAME': '44454D4F',
59+
'NODE_ADDRESS': 'BC8CCDEE1A320000',
60+
'NTS': 'ssdp:alive',
61+
'ROOT_ADDRESS': 'BC8CCDEE1A320000',
62+
'SERVER': 'SSDP,SAMSUNG-AC-RAC_2013',
63+
'SERVICE_NAME': 'ControlServer-MLib',
64+
'SPEC_VER': 'MSpec-2.00'},
65+
'from': ('192.168.1.101', 46247)}]
66+
"""
67+
68+
self.entries = []
69+
servers = []
70+
# setup socket for broadcast
71+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
72+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
73+
sock.settimeout(DISCOVERY_TIMEOUT)
74+
sock.bind(('', DISCOVERY_PORT))
75+
76+
try:
77+
# Broadcast message
78+
sock.sendto(DISCOVERY_MSG, (DISCOVERY_ADDRESS, DISCOVERY_PORT))
79+
80+
# Look for responses from all recipients
81+
while True:
82+
try:
83+
data, server = sock.recvfrom(1024)
84+
data = data.decode('utf-8')
85+
if 'LOCATION:' in data:
86+
data = {k: v.strip() for (k, v) in (
87+
line.split(':', 1) for line in
88+
data.splitlines() if ':' in line)}
89+
if 'NICKNAME' in data:
90+
unhex = binascii.unhexlify(data['NICKNAME'])
91+
data['NICKNAME'] = unhex.decode('utf-8')
92+
if server not in servers:
93+
servers.append(server)
94+
self.entries.append({'data': data, 'from': server})
95+
except socket.timeout:
96+
break
97+
finally:
98+
sock.close()
99+
100+
101+
def main():
102+
"""Test Samsung Smart AC discovery."""
103+
from pprint import pprint
104+
105+
samsungac = SamsungAC()
106+
107+
pprint("Scanning Samsung Smart AC...")
108+
samsungac.update()
109+
pprint(samsungac.entries)
110+
111+
if __name__ == "__main__":
112+
main()

0 commit comments

Comments
 (0)