Skip to content
Snippets Groups Projects
Commit 5d5648c3 authored by jkerdreu's avatar jkerdreu
Browse files

Added devices forced discovery to fix Mael trouble w/ missing devices

git-svn-id: https://redmine.imt-atlantique.fr/svn/xaal/code/Python/trunk@2216 b32b6428-25c9-4566-ad07-03861ab6144f
parent 1a4cd3de
Branches
No related tags found
No related merge requests found
......@@ -14,10 +14,12 @@ AQARA_ENCRYPT_IV = b'\x17\x99\x6d\x09\x3d\x28\xdd\xb3\xba\x69\x5a\x2e\x6f\x58\x5
logger = logging.getLogger(__name__)
class AqaraDev(object):
def __init__(self,sid,model,base_addr):
def __init__(self,sid,model,base_addr,xaal_gw):
self.sid = sid
self.model = model
self.base_addr = base_addr
self.xaal_gw = xaal_gw
# xAAL embeded devices
self.devices = []
logger.info('New AqaraDevice %s %s' % (model,sid))
self.setup()
......@@ -43,6 +45,7 @@ class AqaraDev(object):
if cmd in ['report','heartbeat']:
pload = pkt.get('data',None)
if pload:
# json in json really ? grr
data = ujson.decode(pload)
if cmd == 'report':
self.on_report(data)
......@@ -55,7 +58,7 @@ class AqaraDev(object):
logger.info('Unhandled report %s' % data)
def on_heartbeat(self,data):
print(data)
logger.info('Unhandled heartbeat %s' % data)
class Switch(AqaraDev):
......@@ -106,7 +109,6 @@ class Motion(AqaraDev):
self.devices.append(devices.motion(self.base_addr+'00'))
def on_report(self,data):
print(data)
val = data.get('status',None)
if val and val == 'motion':
self.devices[0].attributes['presence'] = True
......@@ -134,11 +136,14 @@ class Vibration(AqaraDev):pass
class Cube(AqaraDev):pass
class RelayController(AqaraDev):pass
class Gateway(AqaraDev):
def setup(self):
self.sids = []
self.ip = None
self.token = None
self.ready = False
self.rgb = None
self._blink_color = 0
self.connect()
......@@ -152,8 +157,15 @@ class Gateway(AqaraDev):
siren = Device('siren.sound',self.base_addr+'01')
siren.methods['play'] = self.siren_play
siren.methods['stop'] = self.siren_stop
siren.methods['debug'] = self.debug
self.devices.append(siren)
def debug(self):
import pdb;pdb.set_trace()
#========================================================================
## GW Unicast methods
#========================================================================
@property
def secret(self):
return self._secret
......@@ -173,7 +185,7 @@ class Gateway(AqaraDev):
def send(self,pkt):
if not self.ip:
logger.warning("GW not found, please wait")
logger.warning("GW IP not found yet, please wait")
return
try:
#print(pkt)
......@@ -183,20 +195,73 @@ class Gateway(AqaraDev):
except Exception as e:
logger.warning(e)
def write(self,data = {}):
def send_cmd(self,cmd,sid,data={}):
if self.token == None:
logger.warning("No token yet, please wait")
return
key = self.make_key()
data.update({"key":key})
pload = {"cmd":"write",
"sid": self.sid,
pload = {"cmd": cmd,
"sid": sid,
"data": data
}
pkt = ujson.encode(pload).encode('utf8')
return self.send(pkt)
#========================================================================
## Gateway Unicast commands
#========================================================================
def write(self,data = {}):
"""send command to the GW, used for siren, lamp, radio?"""
pkt = self.send_cmd("write",self.sid,data)
self.on_receive(pkt)
def get_id_list(self):
""" retreive the devices list"""
pkt = self.send_cmd("get_id_list",self.sid)
self.on_receive(pkt)
def discover(self):
"""
query device list and ask for a read, on each sid
The doc say we should use {"cmd": "discovery"} instead off get_id_list +
read, but I always have a discory error => missing sid
http://docs.opencloud.aqara.com/en/development/gateway-LAN-communication/
"""
self.get_id_list()
for sid in self.sids:
pkt = self.send_cmd("read",sid)
self.on_receive(pkt)
def on_receive(self,pkt):
if not pkt: return
pload = ujson.decode(pkt)
cmd = pload.get('cmd',None)
if cmd == 'get_id_list_ack':
data = pload.get('data','[]')
self.sids = ujson.decode(data)
if cmd == 'read_ack':
model = pload.get('model',None)
sid = pload.get('sid',None)
dev = self.xaal_gw.get_device(sid,model)
if dev:
data = ujson.decode(pload.get('data','{}'))
dev.on_report(data)
else:
logger.warning("Unknow device %s" % pload)
if cmd == 'write_ack':
print(pkt)
#========================================================================
## RGB Leds
#========================================================================
def get_rgb(self,red,green,blue,brightness=0xFF):
return brightness<<24|( red << 16)|( green << 8)|blue
......@@ -238,7 +303,9 @@ class Gateway(AqaraDev):
eng.add_timer(self.lamp_off,20,0)
eng.add_timer(self._lamp_blink,1,15)
## Siren go here
#========================================================================
## Siren
#========================================================================
def siren_play(self,_sound=2,_volume=5):
logger.info('Playing %s %s' % (_sound,_volume))
data = {
......@@ -252,18 +319,29 @@ class Gateway(AqaraDev):
print(self.write(data))
#========================================================================
## GW Mutlicast messages handlers
#========================================================================
def parse(self,pkt):
# the Gateway need to parse the token addtionnal parameter
# token used on gw aren't in data..
cmd = pkt.get('cmd',None)
if cmd == 'heartbeat':
token = pkt.get('token',None)
if token: self.token = token
if token:
self.token = token
AqaraDev.parse(self,pkt)
def on_heartbeat(self,data):
ip = data.get('ip',None)
if ip and ip != self.ip:
self.ip = ip
logger.info("GW IP found: %s" % ip)
if not self.ready :
if self.token and self.ip:
self.ready = True
self.discover()
def on_report(self,data):
rgb = data.get('rgb',None)
......
......@@ -29,6 +29,8 @@ def find_device_class(model):
return devices.Vibration
if model == 'sensor_cube.aqgl01':
return devices.Cube
if model == 'lumi.ctrl_dualchn':
return devices.RelayController
return None
class GW(gevent.Greenlet):
......@@ -52,7 +54,7 @@ class GW(gevent.Greenlet):
def add_device(self,sid,model,base_addr):
klass = find_device_class(model)
if klass:
dev = klass(sid,model,base_addr)
dev = klass(sid,model,base_addr,self)
self.engine.add_devices(dev.devices)
self.devices.update({sid:dev})
return dev
......@@ -62,6 +64,10 @@ class GW(gevent.Greenlet):
def setup(self):
self.aqara = AqaraConnector()
def load_devices_from_cfg(self):
# not used anymore
self.aqara = AqaraConnector()
devs = self.cfg['devices']
for sid in devs:
cfg = devs[sid]
......@@ -73,13 +79,42 @@ class GW(gevent.Greenlet):
if dev and model == 'gateway':
dev.secret = devs[sid].get('secret',None)
def get_device(self,sid,model):
# Already running device ?
if sid in self.devices.keys():
return self.devices[sid]
# Already known device ?
elif sid in self.cfg['devices'].keys():
cfg = self.cfg['devices'][sid]
model_old = cfg.get('model',None)
base_addr = cfg.get('base_addr',None)
dev = None
if model != model_old:
logger.warn("Device %s wrong model" % sid)
if model and base_addr:
dev = self.add_device(sid,model,base_addr)
if dev and model == 'gateway':
dev.secret = cfg.get('secret',None)
return dev
# Still not found ? => new device
else:
base_addr = tools.get_random_uuid()[:-2]
dev = self.add_device(sid,model,base_addr)
if dev:
cfg = {'base_addr' : base_addr,'model':model}
self.cfg['devices'].update({sid:cfg})
return dev
def _run(self):
while 1:
pkt = self.aqara.receive()
if pkt:
self.handle(pkt)
self.on_receive(pkt)
def handle(self,pkt):
def on_receive(self,pkt):
print(pkt)
sid = pkt.get('sid',None)
if not sid: return
dev = None
......@@ -87,12 +122,10 @@ class GW(gevent.Greenlet):
dev = self.devices[sid]
else:
model = pkt.get('model',None)
if not model:return
base_addr = tools.get_random_uuid()[:-2]
dev = self.add_device(sid,model,base_addr)
if dev:
cfg = {'base_addr' : base_addr,'model':model}
self.cfg['devices'].update({sid:cfg})
if not model:
return
dev = self.get_device(sid,model)
if dev:
dev.parse(pkt)
......
......@@ -11,6 +11,12 @@ logger = logging.getLogger(__name__)
class AqaraConnector:
"""
Aqara Device Report/Heartbeat connector, used only to receive event, if you want to send a command,
you must send a unicast datagram to the right Aqara GW.
Please note, this isn't the Gateway discovery
"""
def __init__(self):
self.nc = network.NetworkConnector('224.0.0.50',9898,10)
self.nc.connect()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment