Beispiel #1
0
class blower():
    #_____________________________________________________________________________
    def __init__(self, machine, name):
        builder.SetDeviceName(name)
        self.com = ModbusClient(host=machine, port=4000, auto_open=True)  #4000
        self.com.mode(constants.MODBUS_RTU)
        stat = self.com.open()
        self.pv_stat = builder.aIn("stat")
        self.pv_stat.PREC = 1
        self.pv_stat.LOPR = 0
        self.pv_stat.HOPR = 100
        self.pv_temp = builder.aIn("temp")
        self.pv_temp.PREC = 1
        self.pv_temp.LOPR = 0
        self.pv_temp.HOPR = 100
        self.pv_humi = builder.aIn("humidity")
        self.pv_humi.PREC = 1
        self.pv_humi.LOPR = 0
        self.pv_humi.HOPR = 100
        self.pv_humi.HSV = "MINOR"
        self.pv_humi.HHSV = "MAJOR"
        self.pv_humi.HIGH = 45
        self.pv_humi.HIHI = 50
        self.pv_flow = builder.aIn("flow")
        self.pv_flow.PREC = 0
        self.pv_flow.LOPR = 0
        self.pv_flow.HOPR = 600
        self.pv_flow.LOLO = 250
        self.pv_flow.LOW = 300
        self.pv_flow.HIGH = 480
        self.pv_flow.HIHI = 520
        self.pv_flow.LSV = "MINOR"
        self.pv_flow.LLSV = "MAJOR"
        self.pv_flow.HSV = "MINOR"
        self.pv_flow.HHSV = "MAJOR"
        self.stat_pv = builder.boolIn("status",
                                      ZNAM="off",
                                      ONAM="on",
                                      DESC=name)
        self.stat_pv.ZSV = "MAJOR"
        self.pv_on = builder.boolOut("on",
                                     ZNAM="0",
                                     ONAM="1",
                                     HIGH=0.1,
                                     on_update=self.turnOn)
        self.pv_off = builder.boolOut("off",
                                      ZNAM="0",
                                      ONAM="1",
                                      HIGH=0.1,
                                      on_update=self.turnOff)
        self.busy = False
        self.pv_act = builder.boolOut("activity", ZNAM="0", ONAM="1", HIGH=1)
        self.pv_was_on = builder.boolOut("was_on",
                                         ZNAM="0",
                                         ONAM="1",
                                         HIGH=1.5)
        self.pv_was_off = builder.boolOut("was_off",
                                          ZNAM="0",
                                          ONAM="1",
                                          HIGH=1.5)
        self.id_temp = 0
        self.id_stat = 1

    #_____________________________________________________________________________
    def start_monit_loop(self):
        t = threading.Thread(target=self.monitor_loop)
        t.daemon = True
        t.start()

    #_____________________________________________________________________________
    def monitor_loop(self):
        while True:
            time.sleep(2)
            try:
                if self.busy == False: self.read_YOGOGAWA()
            except TypeError:
                print "monitor_loop: reading skipped due to external write, busy:", self.busy
                self.busy = False

    #_____________________________________________________________________________
    def get_1dig(self, i):
        #print i
        return float(i) * 0.1

    #_____________________________________________________________________________
    def read_YOGOGAWA(self):

        self.busy = True

        self.com.unit_id(1)
        resp = self.com.read_holding_registers(1, 2)
        self.pv_stat.set(self.get_1dig(resp[self.id_stat]))
        self.pv_temp.set(self.get_1dig(resp[self.id_temp]))
        #self.pv_stat.set(resp[0])
        #self.pv_temp.set(resp[1])
        #self.get_1dig(resp[0])
        #self.get_1dig(resp[1])

        self.com.unit_id(2)
        resp = self.com.read_holding_registers(1, 1)[0]
        self.pv_humi.set(self.get_1dig(resp))

        self.com.unit_id(3)
        self.pv_flow.set(get_2comp(self.com.read_holding_registers(1, 1)[0]))

        self.busy = False

        self.pv_act.set(1)

    #_____________________________________________________________________________
    def turnOn(self, val):
        if val == 0: return
        while self.busy == True:
            print "turnOn: waiting for busy to clear"
            time.sleep(0.2)
        self.busy = True
        self.com.unit_id(5)
        self.com.write_single_coil(0, True)
        self.busy = False
        #print("funtion turnOn done")
        self.pv_was_on.set(1)

    #_____________________________________________________________________________
    def turnOff(self, val):
        if val == 0: return
        while self.busy == True:
            print "turnOff: waiting for busy to clear"
            time.sleep(0.2)
        self.busy = True
        self.com.unit_id(5)
        self.com.write_single_coil(1, True)
        self.busy = False
        #print("function turnOff done")
        self.pv_was_off.set(1)
Beispiel #2
0
from pyModbusTCP.client import ModbusClient
from pyModbusTCP.constants import MODBUS_RTU
from config.modbus import *
from time import sleep
    
### MODBUS INITIALIZATION
c = ModbusClient()
c.mode(MODBUS_RTU) # enable MODBUS_RTU mode
c.timeout(1)
c.debug(True)
c.auto_open(True)
c.auto_close(True)

reg_value={'dum':None} # DUMMY FOR RECORDING REGISTRY LISTS
### END MODBUS INITIALIZATION

### INTERNAL FUNCTIONS
def loc_id(loc):
    device=loc.split('.')
    modmap,conn_id = MODBUS_MAP[device[2]],CONNECTIONS[device[0]][device[1]]
    return modmap,conn_id # [slave_id,reg_values],[IP_addr,port,slave_id_modifier]

def reg_add(modmap,color):
    reg=[0x0,0x0,0x0]
    for cl in COLOR_LIST[color]:
        mapcol=modmap[1][cl] # [0xXXXX,0xXXXX,0xXXXX]
        for i in range(0,3):
            reg[i]+=mapcol[i]
    return reg

def open_comm(): # MODBUS AUTO-RECONNECT
class MitsubishiAirConditioner:
    # 自上次读取一次新信息起经过了多久
    lastRefreshTimestamp = time.time()

    client = None
    messageQueue = None
    messageThread = None

    # 待发送的需要等待回复的命令,成员格式为:{"code":"XX", "cmd":"XXXXXXX", "type": "query", "timestamp": timestamp}
    arrayCmdNeedWait = []

    # 正在等待回应的命令
    dicCmdWaiting = None

    # 存储各个空调控制器的dic, key:字符串表示的控制器模块编码(HEX) value:LJAircon对象
    dicAircon = {}

    # nValue/sValue至寄存器Payload的map - 开关
    mapVPPowerOn = None
    # 寄存器Payload至nValue/sValue的map - 开关
    mapPVPowerOn = None
    # nValue/sValue至寄存器Payload的map - 运行模式
    mapVPMode = None
    # 寄存器Payload至nValue/sValue的map - 运行模式
    mapPVMode = None
    # nValue/sValue至寄存器Payload的map - 风速
    mapVPFanSpeed = None
    # 寄存器Payload至nValue/sValue的map - 风速
    mapPVFanSpeed = None
    # nValue/sValue至寄存器Payload的map - 目标温度
    mapVPSetPoint = None
    # 寄存器Payload至nValue/sValue的map - 目标温度
    mapPVSetPoint = None
    # nValue/sValue至寄存器Payload的map - 室温
    mapVPRoomPoint = None
    # 寄存器Payload至nValue/sValue的map - 室温
    mapPVRoomPoint = None
    # nValue/sValue至寄存器Payload的map - 风向
    mapVPFanDirect = None
    # 寄存器Payload至nValue/sValue的map - 风向
    mapPVFanDirect = None

    def __init__(self):
        self.messageQueue = queue.Queue()
        self.messageThread = threading.Thread(
            name="QueueThread",
            target=MitsubishiAirConditioner.handleMessage,
            args=(self, ))

        # 0:关1:开
        self.mapVPPowerOn = {0: 0, 1: 1}
        self.mapPVPowerOn = self.revertDic(self.mapVPPowerOn)
        # 0:自动,1:制冷,2:送风,3:除湿,4:制热
        self.mapVPMode = {'10': 0, '20': 1, '30': 2, '40': 3, '50': 4}
        self.mapPVMode = self.revertDic(self.mapVPMode)
        # 0:自动,2:低,3:中2,5:中1,6:高
        self.mapVPFanSpeed = {'10': 0, '20': 2, '30': 3, '40': 5, '50': 6}
        self.mapPVFanSpeed = self.revertDic(self.mapVPFanSpeed)
        # 16~31°C (x10),最小单位0.5°C
        self.mapPVSetPoint = {}
        for i in range(190, 300, 5):
            self.mapPVSetPoint[i] = str((int((i - 190) / 5) + 1) * 10)
        self.mapVPSetPoint = self.revertDic(self.mapPVSetPoint)
        # 10~38°C (x10),最小单位1°C
        self.mapPVRoomPoint = {}
        for i in range(100, 385, 5):
            if i % 10 == 0:
                self.mapPVRoomPoint[i] = str(i // 10)
            elif i % 5 == 0:
                self.mapPVRoomPoint[i] = str(float(i) / 10)
        self.mapVPRoomPoint = self.revertDic(self.mapPVRoomPoint)
        # 0:自动,1~5:位置1~5 7:摆风
        self.mapVPFanDirect = {
            '10': 0,
            '20': 1,
            '30': 2,
            '40': 3,
            '50': 4,
            '60': 5,
            '70': 7
        }
        self.mapPVFanDirect = self.revertDic(self.mapVPFanDirect)
        return

    def onStart(self):
        Domoticz.Heartbeat(5)

        if Parameters["Mode2"] == "Debug":
            Domoticz.Debugging(1)
        else:
            Domoticz.Debugging(0)

        # 从Domoticz重新加载硬件和设备信息
        self.reloadFromDomoticz()

        debug = False
        if Parameters["Mode2"] == "Debug":
            debug = True
        if self.client and self.client.is_open():
            self.client.close()
        self.client = ModbusClient(host=Parameters["Address"],
                                   port=Parameters["Port"],
                                   auto_open=True,
                                   auto_close=False,
                                   timeout=1)
        self.messageThread.start()
        self.client.mode(2)

        self.messageQueue.put({
            "Type": "Log",
            "Text": "Heartbeat test message"
        })

    def onStop(self):
        # signal queue thread to exit
        self.messageQueue.put(None)
        Domoticz.Log("Clearing message queue...")
        self.messageQueue.join()

        # Wait until queue thread has exited
        Domoticz.Log("Threads still active: " + str(threading.active_count()) +
                     ", should be 1.")
        while (threading.active_count() > 1):
            for thread in threading.enumerate():
                if (thread.name != threading.current_thread().name):
                    Domoticz.Log(
                        "'" + thread.name +
                        "' is still running, waiting otherwise Domoticz will abort on plugin exit."
                    )
            time.sleep(0.1)
        return

    def onConnect(self, Connection, Status, Description):
        return

    def onMessage(self, Connection, Data):
        return

    def clientConnected(self):
        if not self.client: return False

        if self.client.is_open():
            return True
        elif not self.client.open():
            Domoticz.Log('Warning: Modbus connect failed')
            return False

    def handleMessage(self):
        try:
            Domoticz.Debug("Entering message handler")
            while True:
                Message = self.messageQueue.get(block=True)
                if Message is None:
                    Domoticz.Debug("Exiting message handler")
                    self.messageQueue.task_done()
                    break

                self.queryStatus()
                self.messageQueue.task_done()
        except Exception as err:
            Domoticz.Error("handleMessage: " + str(err))

    def queryStatus(self):
        if not self.clientConnected():
            for aircon in self.dicAircon.values():
                aircon.goOffline()
            return

        for aircon in self.dicAircon.values():
            #if not aircon.online:
            #continue
            # 设备已连接才发送查询指令

            dicOptions = aircon.devicePowerOn.Options
            if not dicOptions or 'LJCode' not in dicOptions or 'LJShift' not in dicOptions:
                return
            self.client.unit_id(int(dicOptions['LJCode'], 16))
            time.sleep(0.2)

            regs = self.client.read_holding_registers(0, 7)
            if not regs or regs is None:
                Domoticz.Log('Warning: Reading Regs Fail! 0x' +
                             dicOptions['LJCode'])
                aircon.goOffline()
                continue
            elif len(regs) != 7:
                Domoticz.Log(
                    'Warning: Reading Regs Fail! 0x{}, recevied:{}'.format(
                        dicOptions['LJCode'], str(regs)))
                aircon.goOffline()
                continue

            aircon.goOnline()
            Domoticz.Debug('Receive Regs:' + str(regs))
            if aircon.devicePowerOn and regs[0] in self.mapPVPowerOn:
                nValue = self.mapPVPowerOn[regs[0]]
                sValue = aircon.devicePowerOn.sValue
                device = aircon.devicePowerOn
                UpdateDevice(Unit=int(device.Options['LJUnit']),
                             nValue=nValue,
                             sValue=sValue,
                             TimedOut=0)

            if aircon.deviceMode and regs[1] in self.mapPVMode:
                nValue = 1
                sValue = self.mapPVMode[regs[1]]
                device = aircon.deviceMode
                UpdateDevice(Unit=int(device.Options['LJUnit']),
                             nValue=nValue,
                             sValue=sValue,
                             TimedOut=0)

            if aircon.deviceFanSpeed and regs[2] in self.mapPVFanSpeed:
                nValue = 1
                sValue = self.mapPVFanSpeed[regs[2]]
                device = aircon.deviceFanSpeed
                UpdateDevice(Unit=int(device.Options['LJUnit']),
                             nValue=nValue,
                             sValue=sValue,
                             TimedOut=0)

            if aircon.deviceSetPoint and regs[3] in self.mapPVSetPoint:
                nValue = 1
                sValue = self.mapPVSetPoint[regs[3]]
                device = aircon.deviceSetPoint
                UpdateDevice(Unit=int(device.Options['LJUnit']),
                             nValue=nValue,
                             sValue=sValue,
                             TimedOut=0)

            if aircon.deviceRoomPoint and regs[4] in self.mapPVRoomPoint:
                nValue = 1
                sValue = self.mapPVRoomPoint[regs[4]]
                device = aircon.deviceRoomPoint
                UpdateDevice(Unit=int(device.Options['LJUnit']),
                             nValue=nValue,
                             sValue=sValue,
                             TimedOut=0)

            if aircon.deviceFanDirect and regs[5] in self.mapPVFanDirect:
                nValue = 1
                sValue = self.mapPVFanDirect[regs[5]]
                device = aircon.deviceFanDirect
                UpdateDevice(Unit=int(device.Options['LJUnit']),
                             nValue=nValue,
                             sValue=sValue,
                             TimedOut=0)

            if aircon.deviceFaultCode:
                nValue = 1
                hexText = str(hex(regs[6]))
                if len(hexText) >= 4 and hexText[-4:] == '8000':
                    sValue = '运行正常'
                else:
                    sValue = '错误!故障代码: ' + hexText
                device = aircon.deviceFaultCode
                UpdateDevice(Unit=int(device.Options['LJUnit']),
                             nValue=nValue,
                             sValue=sValue,
                             TimedOut=0)

    def onCommand(self, Unit, Command, Level, Hue):
        if not self.clientConnected():
            Domoticz.Log('Modbus connect failed!')
            for aircon in self.dicAircon.values():
                aircon.goOffline()
            return
        Domoticz.Log("onCommand called for Unit " + str(Unit) +
                     ": Parameter '" + str(Command) + "', Level: " +
                     str(Level))
        Command = Command.strip()
        action, sep, params = Command.partition(' ')
        action = action.capitalize()
        params = params.capitalize()
        device = Devices[Unit]
        options = device.Options
        if not options or 'LJCode' not in options or 'LJShift' not in options or 'LJUnit' not in options:
            return
        code = device.Options['LJCode']
        shift = device.Options['LJShift']

        if not code or code not in self.dicAircon or not shift or int(
                shift) < 0 or int(shift) > 6:
            return
        aircon = self.dicAircon[code]
        #if not aircon.online:
        #return

        if shift == '00':
            # 开关
            if action == 'On':
                nValue = 1
            elif action == 'Off':
                nValue = 0
            self.sendCmdByNValue(aircon, self.mapVPPowerOn,
                                 aircon.devicePowerOn, nValue)

        elif shift == '01':
            # 模式
            if action == 'Set' and params == 'Level':
                if aircon.devicePowerOn.nValue == 0:
                    # 关机状态,先开机 #TODO测试连续写
                    self.sendCmdByNValue(aircon, self.mapVPPowerOn,
                                         aircon.devicePowerOn, 1)
                self.sendCmdBySValue(aircon, self.mapVPMode, aircon.deviceMode,
                                     str(Level))
        elif shift == '02':
            # 风速
            if action == 'Set' and params == 'Level':
                if aircon.devicePowerOn.nValue == 0:
                    # 关机状态,先开机
                    self.sendCmdByNValue(aircon, self.mapVPPowerOn,
                                         aircon.devicePowerOn, 1)
                self.sendCmdBySValue(aircon, self.mapVPFanSpeed,
                                     aircon.deviceFanSpeed, str(Level))
        elif shift == '03':
            # 温度
            if action == 'Set' and params == 'Level':
                if aircon.devicePowerOn.nValue == 0:
                    # 关机状态,先开机
                    self.sendCmdByNValue(aircon, self.mapVPPowerOn,
                                         aircon.devicePowerOn, 1)
                self.sendCmdBySValue(aircon, self.mapVPSetPoint,
                                     aircon.deviceSetPoint, str(Level))
        elif shift == '04':
            # 室温
            if action == 'Set' and params == 'Level':
                if aircon.devicePowerOn.nValue == 0:
                    # 关机状态,先开机
                    self.sendCmdByNValue(aircon, self.mapVPPowerOn,
                                         aircon.devicePowerOn, 1)
                self.sendCmdBySValue(aircon, self.mapVPRoomPoint,
                                     aircon.deviceRoomPoint, str(Level))
        elif shift == '05':
            # 风向
            if action == 'Set' and params == 'Level':
                if aircon.devicePowerOn.nValue == 0:
                    # 关机状态,先开机
                    self.sendCmdByNValue(aircon, self.mapVPPowerOn,
                                         aircon.devicePowerOn, 1)
                self.sendCmdBySValue(aircon, self.mapVPFanDirect,
                                     aircon.deviceFanDirect, str(Level))

    # 从sValue取值,找Payload,并写寄存器
    def sendCmdBySValue(self, aircon, mapVP, device, sValue):
        if not self.clientConnected(): return
        Domoticz.Log('sendCmdBySValue\(mapVP={}, device={}, sValue={}'.format(
            mapVP, device, sValue))  # TODO
        if not device or not mapVP or sValue not in mapVP:
            return None
        registerText = device.Options['LJShift']
        self.client.unit_id(int(device.Options['LJCode'], 16))
        if (self.client.write_single_register(int(registerText, 16),
                                              mapVP[str(sValue)])):
            Domoticz.Log('write_single_register\(0x{}, {}\) success!'.format(
                registerText, mapVP[sValue]))  # TODO
            timedOut = 0
            result = True
        else:
            Domoticz.Log('write_single_register\(0x{}, {}\) failed!'.format(
                registerText, mapVP[sValue]))  # TODO
            timedOut = 1
            result = False
            aircon.goOffline()
            sValue = device.sValue

        UpdateDevice(Unit=int(device.Options['LJUnit']),
                     nValue=device.nValue,
                     sValue=str(sValue),
                     TimedOut=timedOut)
        return result

    # 从nValue取值,找Payload,并写寄存器
    def sendCmdByNValue(self, aircon, mapVP, device, nValue):
        if not self.clientConnected(): return
        Domoticz.Log('sendCmdByNValue\(mapVP={}, device={}, nValue={}'.format(
            mapVP, device, nValue))  # TODO
        if not device or not mapVP or nValue not in mapVP:
            return None
        registerText = device.Options['LJShift']
        self.client.unit_id(int(device.Options['LJCode'], 16))
        if (self.client.write_single_register(int(registerText, 16),
                                              mapVP[nValue])):
            Domoticz.Log('write_single_register\(0x{}, {}\) success!'.format(
                registerText, mapVP[nValue]))  # TODO
            timedOut = 0
            result = True
        else:
            Domoticz.Log('write_single_register\(0x{}, {}\) failed!'.format(
                registerText, mapVP[nValue]))  # TODO
            timedOut = 1
            result = False
            aircon.goOffline()
            nValue = device.nValue
        UpdateDevice(Unit=int(device.Options['LJUnit']),
                     nValue=nValue,
                     sValue=str(device.sValue),
                     TimedOut=timedOut)
        return result

    def onNotification(self, Name, Subject, Text, Status, Priority, Sound,
                       ImageFile):
        Domoticz.Log("Notification: " + Name + "," + Subject + "," + Text +
                     "," + Status + "," + str(Priority) + "," + Sound + "," +
                     ImageFile)

    def onDisconnect(self, Connection):
        Domoticz.Log("onDisconnect called")

    def onHeartbeat(self):
        # Domoticz.Log('onHeartbeat Called ---------------------------------------')
        # 如果没连接则尝试重新连接
        if not self.clientConnected():
            for aircon in self.dicAircon.values():
                aircon.goOffline()
            return

        # 查询空调状态
        self.messageQueue.put({
            "Type": "Log",
            "Text": "Heartbeat test message"
        })

    def reloadFromDomoticz(self):
        self.dicAircon = {}
        strListCode = Parameters["Mode1"]
        strListCode = strListCode.replace(',', '')
        strListCode = strListCode.replace('|', '')
        strListCode = strListCode.replace(' ', '')
        strListCode = strListCode.replace('0X', '0x')
        strListCode = strListCode.replace('X', '0x')
        setCode = set(strListCode.split('0x'))
        setCode2 = set([])
        for tmp in setCode:
            if not tmp:
                continue
            setCode2.add(tmp.upper())
        for tmp2 in setCode2:
            if not tmp2:
                continue
            if len(tmp2) > 2: tmp2 = tmp2[-2:]
            tmp2 = '{:0>2}'.format(tmp2)
            Domoticz.Log('Detected Code:' + tmp2)
            self.dicAircon[tmp2] = LJAircon(tmp2)

        # 记录已有的unit
        setUnit = set([])
        # 待删除的device对应的unit
        setUnitDel = set([])
        # 所有的Unit集合
        setUnitAll = set(range(1, 256))
        # 将Device放入对应的控制器对象中,多余的device删除
        for unit in Devices:
            device = Devices[unit]
            dicOptions = device.Options
            Domoticz.Log("DEVICE FROM PANEL " +
                         descDevice(device=device, unit=unit))

            shouldDelete = False
            if dicOptions and 'LJCode' in dicOptions and 'LJShift' in dicOptions and dicOptions[
                    'LJCode'] in self.dicAircon:
                # 有匹配的控制器,赋值
                aircon = self.dicAircon[dicOptions['LJCode']]
                if dicOptions['LJShift'] == '00':
                    # 开关
                    if aircon.devicePowerOn:
                        #已经有现成的设备,加入待删除
                        Domoticz.Log(
                            'Already have devicePowerOn, add to delete list. '
                            + device.Name)
                        shouldDelete = True
                    else:
                        aircon.devicePowerOn = device
                        aircon.dicDevice[unit] = device
                elif dicOptions['LJShift'] == '01':
                    # 运行模式
                    if aircon.deviceMode:
                        #已经有现成的设备,加入待删除
                        Domoticz.Log(
                            'Already have deviceMode, add to delete list. ' +
                            device.Name)
                        shouldDelete = True
                    else:
                        aircon.deviceMode = device
                        aircon.dicDevice[unit] = device
                elif dicOptions['LJShift'] == '02':
                    # 风速
                    if aircon.deviceFanSpeed:
                        #已经有现成的设备,加入待删除
                        Domoticz.Log(
                            'Already have deviceFanSpeed, add to delete list. '
                            + device.Name)
                        shouldDelete = True
                    else:
                        aircon.deviceFanSpeed = device
                        aircon.dicDevice[unit] = device
                elif dicOptions['LJShift'] == '03':
                    # 目标温度
                    if aircon.deviceSetPoint:
                        #已经有现成的设备,加入待删除
                        Domoticz.Log(
                            'Already have deviceSetPoint, add to delete list. '
                            + device.Name)
                        shouldDelete = True
                    else:
                        aircon.deviceSetPoint = device
                        aircon.dicDevice[unit] = device
                elif dicOptions['LJShift'] == '04':
                    # 室温
                    if aircon.deviceRoomPoint:
                        #已经有现成的设备,加入待删除
                        Domoticz.Log(
                            'Already have deviceRoomPoint, add to delete list. '
                            + device.Name)
                        shouldDelete = True
                    else:
                        aircon.deviceRoomPoint = device
                        aircon.dicDevice[unit] = device
                elif dicOptions['LJShift'] == '05':
                    # 风向
                    if aircon.deviceFanDirect:
                        #已经有现成的设备,加入待删除
                        Domoticz.Log(
                            'Already have deviceFanDirect, add to delete list. '
                            + device.Name)
                        shouldDelete = True
                    else:
                        aircon.deviceFanDirect = device
                        aircon.dicDevice[unit] = device
                elif dicOptions['LJShift'] == '06':
                    # 状态
                    if aircon.deviceFaultCode:
                        #已经有现成的设备,加入待删除
                        Domoticz.Log(
                            'Already have deviceFaultCode, add to delete list. '
                            + device.Name)
                        shouldDelete = True
                    else:
                        aircon.deviceFaultCode = device
                        aircon.dicDevice[unit] = device
                else:
                    shouldDelete = True
            else:
                shouldDelete = True

            if shouldDelete:
                setUnitDel.add(unit)
            else:
                setUnit.add(unit)
        Domoticz.Log("DELETE DEVICES IN UNIT: " + str(setUnitDel))

        # 删除多余的Device
        for unit in setUnitDel:
            Devices[unit].Delete()

        # Check if images are in database
        #if "LJCountDown" not in Images:
        #    Domoticz.Image("LJCountDown.zip").Create()
        #image = Images["LJCountDown"].ID

        # 遍历控制器,补全控制器对应的device
        for aircon in self.dicAircon.values():
            setAvariable = setUnitAll.difference(setUnit)
            if not setAvariable or len(setAvariable) == 0:
                continue
            if not aircon.devicePowerOn:
                newUnit = setAvariable.pop()
                setUnit.add(newUnit)
                optionsCustom = {
                    "LJUnit": str(newUnit),
                    'LJCode': aircon.code,
                    'LJShift': '00'
                }
                name = '0x{} 开关'.format(aircon.code)
                aircon.devicePowerOn = Domoticz.Device(Name=name,
                                                       Unit=newUnit,
                                                       Type=244,
                                                       Subtype=73,
                                                       Switchtype=0,
                                                       Options=optionsCustom)
                aircon.devicePowerOn.Create()
                aircon.dicDevice[newUnit] = aircon.devicePowerOn
                Domoticz.Log(
                    'ADD DEVICE :' +
                    descDevice(device=aircon.devicePowerOn, unit=newUnit))
            if not aircon.deviceMode and len(setAvariable) > 0:
                newUnit = setAvariable.pop()
                setUnit.add(newUnit)
                optionsCustom = {
                    "LJUnit": str(newUnit),
                    'LJCode': aircon.code,
                    'LJShift': '01'
                }
                levelNames = 'Off|自动|制冷|送风|除湿|制热'
                optionsGradient = {
                    'LevelActions': '|' * levelNames.count('|'),
                    'LevelNames': levelNames,
                    'LevelOffHidden': 'true',
                    'SelectorStyle': '0'
                }
                name = '0x{} 模式'.format(aircon.code)
                aircon.deviceMode = Domoticz.Device(Name=name,
                                                    Unit=newUnit,
                                                    TypeName="Selector Switch",
                                                    Switchtype=18,
                                                    Image=0,
                                                    Options=dict(
                                                        optionsCustom,
                                                        **optionsGradient))
                aircon.deviceMode.Create()
                aircon.dicDevice[newUnit] = aircon.deviceMode
                Domoticz.Log(
                    'ADD DEVICE :' +
                    descDevice(device=aircon.deviceMode, unit=newUnit))
            if not aircon.deviceFanSpeed and len(setAvariable) > 0:
                newUnit = setAvariable.pop()
                setUnit.add(newUnit)
                optionsCustom = {
                    "LJUnit": str(newUnit),
                    'LJCode': aircon.code,
                    'LJShift': '02'
                }
                levelNames = 'Off|自动|低|中2|中1|高'
                optionsGradient = {
                    'LevelActions': '|' * levelNames.count('|'),
                    'LevelNames': levelNames,
                    'LevelOffHidden': 'true',
                    'SelectorStyle': '0'
                }
                name = '0x{} 风速'.format(aircon.code)
                aircon.deviceFanSpeed = Domoticz.Device(
                    Name=name,
                    Unit=newUnit,
                    TypeName="Selector Switch",
                    Switchtype=18,
                    Image=0,
                    Options=dict(optionsCustom, **optionsGradient))
                aircon.deviceFanSpeed.Create()
                aircon.dicDevice[newUnit] = aircon.deviceFanSpeed
                Domoticz.Log(
                    'ADD DEVICE :' +
                    descDevice(device=aircon.deviceFanSpeed, unit=newUnit))
            if not aircon.deviceSetPoint and len(setAvariable) > 0:
                newUnit = setAvariable.pop()
                setUnit.add(newUnit)
                optionsCustom = {
                    "LJUnit": str(newUnit),
                    'LJCode': aircon.code,
                    'LJShift': '03'
                }
                levelNames = 'Off'
                for i in range(190, 300, 5):
                    if i % 10 == 0:
                        levelNames += '|' + str(i // 10) + '℃'
                    elif i % 5 == 0:
                        levelNames += '|' + str(float(i) / 10) + '℃'

                optionsGradient = {
                    'LevelActions': '|' * levelNames.count('|'),
                    'LevelNames': levelNames,
                    'LevelOffHidden': 'true',
                    'SelectorStyle': '1'
                }
                name = '0x{} 设定温度'.format(aircon.code)
                aircon.deviceSetPoint = Domoticz.Device(
                    Name=name,
                    Unit=newUnit,
                    TypeName="Selector Switch",
                    Switchtype=18,
                    Image=0,
                    Options=dict(optionsCustom, **optionsGradient))
                aircon.deviceSetPoint.Create()
                aircon.dicDevice[newUnit] = aircon.deviceSetPoint
                Domoticz.Log(
                    'ADD DEVICE :' +
                    descDevice(device=aircon.deviceSetPoint, unit=newUnit))
            if not aircon.deviceRoomPoint and len(setAvariable) > 0:
                newUnit = setAvariable.pop()
                setUnit.add(newUnit)
                optionsCustom = {
                    "LJUnit": str(newUnit),
                    'LJCode': aircon.code,
                    'LJShift': '04'
                }
                name = '0x{} 室温'.format(aircon.code)
                aircon.deviceRoomPoint = Domoticz.Device(
                    Name=name,
                    Unit=newUnit,
                    TypeName="Temperature",
                    Options=optionsCustom)
                aircon.deviceRoomPoint.Create()
                aircon.dicDevice[newUnit] = aircon.deviceRoomPoint
                Domoticz.Log(
                    'ADD DEVICE :' +
                    descDevice(device=aircon.deviceRoomPoint, unit=newUnit))
            if not aircon.deviceFanDirect and len(setAvariable) > 0:
                newUnit = setAvariable.pop()
                setUnit.add(newUnit)
                optionsCustom = {
                    "LJUnit": str(newUnit),
                    'LJCode': aircon.code,
                    'LJShift': '05'
                }
                levelNames = 'Off|自动|位置1|位置2|位置3|位置4|位置5|摆风'
                optionsGradient = {
                    'LevelActions': '|' * levelNames.count('|'),
                    'LevelNames': levelNames,
                    'LevelOffHidden': 'true',
                    'SelectorStyle': '0'
                }
                name = '0x{} 风向'.format(aircon.code)
                aircon.deviceFanDirect = Domoticz.Device(
                    Name=name,
                    Unit=newUnit,
                    TypeName="Selector Switch",
                    Switchtype=18,
                    Image=0,
                    Options=dict(optionsCustom, **optionsGradient))
                aircon.deviceFanDirect.Create()
                aircon.dicDevice[newUnit] = aircon.deviceFanDirect
                Domoticz.Log(
                    'ADD DEVICE :' +
                    descDevice(device=aircon.deviceFanDirect, unit=newUnit))
            if not aircon.deviceFaultCode and len(setAvariable) > 0:
                newUnit = setAvariable.pop()
                setUnit.add(newUnit)
                optionsCustom = {
                    "LJUnit": str(newUnit),
                    'LJCode': aircon.code,
                    'LJShift': '06'
                }
                name = '0x{} 状态'.format(aircon.code)
                aircon.deviceRoomPoint = Domoticz.Device(Name=name,
                                                         Unit=newUnit,
                                                         TypeName="Text",
                                                         Image=17,
                                                         Options=optionsCustom)
                aircon.deviceRoomPoint.Create()
                aircon.dicDevice[newUnit] = aircon.deviceRoomPoint
                Domoticz.Log(
                    'ADD DEVICE :' +
                    descDevice(device=aircon.deviceRoomPoint, unit=newUnit))

    def revertDic(self, dic):
        if dic:
            return {v: k for k, v in dic.items()}
        return None