# coding: utf-8 import logging import globals from notification import Notification import binascii import utils class WiserShutter(): def __init__(self): self.name = 'wisershutter' self.ignoreRepeat = False self.handles = { # standard fields 'device_name' : '0x03', 'apparence' : '0x05', 'model_number': '0x08', 'firmware_revision':'0x0a', 'manufacturer_name':'0x0c', # schneider fields 'request_service' :'0x0f', 'response_service' :'0x12', 'notice_service' :'0x16', 'firmware_version' :'0x1a', 'ble_control_point':'0x1d', 'access_level' :'0x20', # 0 = not paired, 1 = paired # probably firmware update fields (no name in gatt): 0x25 + 0x27 # control fields 'on_off' :'0x2b', 'set_level' :'0x2f', 'relative_operations':'0x33', 'blue_roc' :'0x37' } self.notifications = { 'response_service' :'0x13', 'notice_service' :'0x17', 'access_level' :'0x21', 'on_off' :'0x2c', 'set_level' :'0x30', 'relative_operations':'0x34', 'blue_roc' :'0x38' } self.statusNames = { 'opened' : 'Ouvert', 'closed' : 'Fermé', 'opening' : 'Ouverture', 'closing' : 'Fermeture', 'middle' : 'Mi ouvert', 'unknown' : 'Inconnu' } self.notifier = None def enableNotifications(self,conn,mac): # if 'enabled_notifications' in globals.KNOWN_DEVICES[mac.upper()]: if self.notifier != None: logging.debug('WiserShutter--enableNotifications() : Notifications already enabled for %s !',conn.mac) return True if conn and conn.isconnected: writed = conn.writeCharacteristic(self.notifications['set_level'],'0100') logging.debug('WiserShutter--enableNotifications() : writed: %s',str(writed)) writed = conn.writeCharacteristic(self.notifications['relative_operations'],'0100') logging.debug('WiserShutter--enableNotifications() : writed: %s',str(writed)) self.notifier = Notification(conn,WiserShutter) self.notifier.subscribe() logging.debug('WiserShutter--enableNotifications() : Notifications now enabled for %s !',conn.mac) # globals.KNOWN_DEVICES[mac.upper()]['enabled_notifications'] = True return True else: logging.warning("WiserShutter--enableNotifications() : Can't enable notifications on disconnected device !") return False def sendConfigTime(self,mac,conn,value): logging.debug('WiserShutter--sendConfigTime() : Configure time to %d',value) valInt = int(value) minutes = int(valInt / 100) secondes = valInt % 100 valSend = '{:0>4X}'.format(minutes * 60 + secondes) written = conn.writeCharacteristic(self.handles['request_service'],'FFFF09FF108000000102'+valSend,retry=3,response=True) logging.debug('WiserShutter--sendConfigTime() : Configured time to %d=%s',valSend,str(written)) def sendCommand(self,mac,action,value = None): result={} conn=None try: globals.PENDING_ACTION = True globals.KNOWN_DEVICES[mac.upper()]['islocked'] = 1 logging.debug('WiserShutter--sendCommand() : %s on %s', action, mac) (conn, reuse) = utils.getConnection(mac) if conn: #♣ self.enableNotifications(conn, mac) written = None if action == 'up': logging.debug('WiserShutter--sendCommand() : UP') written = conn.writeCharacteristic(self.handles['relative_operations'],'01',retry=3,response=True) elif action == 'down': logging.debug('WiserShutter--sendCommand() : DOWN') written = conn.writeCharacteristic(self.handles['relative_operations'],'02',retry=3,response=True) elif action == 'stop': logging.debug('WiserShutter--sendCommand() : STOP') written = conn.writeCharacteristic(self.handles['relative_operations'],'00',retry=3,response=True) elif action == 'position': if value == None: logging.error('WiserShutter--sendCommand() : Missing Position in command !') else: hexa = hex(int(value) * 100) val = hexa[4:6] + hexa[2:4] logging.debug('WiserShutter--sendCommand() : Position %s ',val) written = conn.writeCharacteristic(self.handles['set_level'],val,retry=3,response=True) elif action == 'time_movement': if value == None: logging.error('WiserShutter--sendCommand() : Missing Time in command !') else: self.sendConfigTime(mac, conn, value) logging.debug('WiserShutter--sendCommand() : action writed: %s', str(written)) else: logging.debug('WiserShutter--sendCommand() : Failed to initConnection for %s', mac) except Exception as e: logging.exception(e) # if conn and conn.isconnected: # logging.debug('WiserShutter--sendCommand() close connection') # conn.disconnect() finally: globals.PENDING_ACTION = False # globals.KNOWN_DEVICES[mac.upper()]['islocked'] = 0 return result def isvalid(self,name,manuf='',data='',mac=''): if name.lower() == self.name or manuf[0:14] == 'b602010020000b': logging.debug('WiserShutter--isValid()=true : name=%s manuf=%s data=%s mac=%s', name, manuf, data, mac) return True return False def parse(self,data,mac,name,manuf): result={} logging.debug('WiserShutter--parse() : name=%s manuf=%s data=%s mac=%s', name, manuf, data, mac) operation = manuf[16:18] position = manuf[18:20] logging.debug('WiserShutter--parse() : operation=%s position=%s',operation,position) status = None if operation == '00': if position == '00': status = 'opened' elif position == 'ff': status = 'closed' else: status = 'middle' elif operation =='01': status = 'opening' elif operation =='02': status = 'closing' elif operation =='03': result['button'] = 'up_short' elif operation =='04': result['button'] = 'down_short' if status in ['opened','closed','middle']: globals.KNOWN_DEVICES[mac.upper()]['islocked'] = 0 if status != None: result['status'] = status result['label'] = self.statusNames[status] result['position'] = int((int(position, 16) / 255.0)*100.0) result['present'] = 1 result['id'] = mac logging.debug("WiserShutter--parse() : %s", str(result)) return result def action(self,message): logging.debug("WiserShutter--action(): %s", str(message)) mac = message['device']['id'] #if not('paired' in message['device']) or message['device']['paired'] == False: # logging.error("Action received on not appaired shutter %s, do nothing !", mac) # return result action = message['command']['action'] if action == 'up': self.sendCommand(mac, 'up') elif action == 'down': self.sendCommand(mac, 'down') elif action == 'stop': self.sendCommand(mac, 'stop') elif action == 'position': value = message['command']['value'] self.sendCommand(mac, 'position', value) elif action == 'time_movement': value = message['command']['value'] self.sendCommand(mac, 'time_movement', value) else: logging.warning('WiserShutter--action() : Action %s unknown for %s', action, mac) def read(self,mac): result={} conn = None try: globals.PENDING_ACTION = True globals.KNOWN_DEVICES[mac.upper()]['islocked'] = 1 logging.debug('WiserShutter--read() : initConnection() pour %s ', mac) (conn, reuse) = utils.getConnection(mac) if conn: name = bytearray(conn.readCharacteristic(self.handles['device_name'])) level = bytearray(conn.readCharacteristic(self.handles['access_level'])) logging.debug('WiserShutter--read()=success : name=%s level=%s', name.decode(), str(level[0])) paired = bool(level[0]) result['id'] = mac result['present'] = 1 result['paired'] = paired result['localname'] = name.decode() else: result['id'] = mac result['present'] = 0 logging.debug('WiserShutter--read() : Failed to initConnection for %s', mac) except Exception as e: logging.exception(e) finally: globals.PENDING_ACTION = False globals.KNOWN_DEVICES[mac.upper()]['islocked'] = 0 if conn.isconnected: logging.debug('WiserShutter--sendCommand() close connection') conn.disconnect() return result def handlenotification(self,conn,cHandle,data,action={}): result={} try: hexHandle = hex(cHandle) logging.debug('WiserShutter--handlenotification() %s : %s',hexHandle, binascii.hexlify(data)) # prevStatus = 'unknown' # if 'status' in globals.KNOWN_DEVICES[conn.mac.upper()]: # prevStatus = globals.KNOWN_DEVICES[conn.mac.upper()]['status'] newStatus = None if hexHandle == self.handles['relative_operations']: val = data[0] if val == 0: logging.debug("WiserShutter--Arret") # if prevStatus in ['opening','closing']: newStatus = 'middle' elif val == 1: logging.debug("WiserShutter--Ouverture") newStatus = 'opening' elif val == 2: logging.debug("WiserShutter--Fermeture") newStatus = 'closing' elif val == 3: logging.debug("WiserShutter--Appui haut court") elif val == 4: logging.debug("WiserShutter--Appui bas court") elif hexHandle == self.handles['set_level']: val = data[1]*256 + data[0] if val == 0: newStatus = 'opened' elif val == 0x2710: # = 10 000 newStatus = 'closed' result['position'] = int(val / 100) logging.debug("WiserShutter--handlenotification() : position=%d",result['position']) if newStatus != None: result['status'] = newStatus result['label'] = self.statusNames[newStatus] # newStatus = prevStatus # else: # globals.KNOWN_DEVICES[conn.mac.upper()]['status'] = newStatus if newStatus in ['opened', 'closed', 'middle']: globals.KNOWN_DEVICES[conn.mac.upper()]['islocked'] = 0 result['id'] = conn.mac result['present'] = 1 globals.JEEDOM_COM.add_changes('devices::'+conn.mac,result) except Exception as e: logging.exception(e) globals.COMPATIBILITY.append(WiserShutter)