View difference between Paste ID: 3xy6Nr4q and BQGD2i0Z
SHOW: | | - or go back to the newest paste.
1
#           Broadlink RM2 Python Plugin for Domoticz
2
#
3
#           Dev. Platform : Win10 x64 & Py 3.5.3 x86
4
#
5-
#           Author:     zak45, 2017
5+
#           Author:     zak45, 2017-2018
6
#           1.1.0:  code compatible py  3.x
7-
#   Send learned command stored in ini file
7+
#           2.0.0:  import from e-Control or any other ini file with similar structure
8-
#   One param required: ini file full path name
8+
#                   webserver for file transfer
9-
#   Can be used stand alone
9+
#                   Off action managed for generated devices
10
#                   clean action to erase files from import folder
11
#           3.0.0:  Add Remote Control device with custom codes on ini file
12
#                   Broadlink lib to v 0.5.0: timeout error solved, Pad the payload for AES encryption (16) (TC2 switch), Add support for pure python AES implementation ...
13
#           4.0.0:  Add eSensor / Smart plug / Multi plug devices
14
#                       --> big thanks to deennoo to have provided test devices  : http://domo-attitude.fr
15
#                   support for chinese char thanks to : https://zhougy0717.github.io/2017/07/31/Smart%20Home%20with%20Domoticz%20+%20BroadLink%20+%20Synology%20+%20Amazon%20Echo/
16
#                   Change the way to retreive module by using 'site', no more 'copy' should be necessary specially for linux systems
17
#                   Broadlink lib to v 0.6.0
18
#                   socket.setdefaulttimeout put on comment as plugin framework is on trouble on onheartbeat(): even with other plugins.
19
#
20
21-
brohost = ""
21+
# Below is what will be displayed in Domoticz GUI under HW
22-
bromac = ""
22+
23
"""
24
<plugin key="BroadlinkRM2" name="Broadlink RM2 with Kodi Remote" author="zak45" version="4.0.0" wikilink="http://www.domoticz.com/wiki/plugins/BroadlinkRM2.html" externallink="https://github.com/mjg59/python-broadlink">
25
    <params>
26
        <param field="Address" label="IP Address" width="200px" required="true" default="127.0.0.1"/>
27
        <param field="Mode1" label="Mac" width="100px" required="true" default="000000000000"/>
28
        <param field="Mode3" label="Device Type" width="250px" required="true"  default="DIS">
29
            <options>
30-
        print('Nothing to send')
30+
                <option label= "Discovery" value="DIS"/>
31-
        sys.exit(3)
31+
                <option label= "Remote Control RM2/RM mini3" value="RM2"/>
32
                <option label= "Remote Control RM2/RM mini3 with Temperature device" value="RM2T"/>
33
                <option label= "eSensor multi sensors A1" value="A1"/>
34
                <option label= "SmartPlug 1" value="SP1"/>
35
                <option label= "SmartPlug 2/3" value="SP2"/>
36
                <option label= "SmartPlug 3S" value="SP3S"/>
37-
        print( "Code Sent....")
37+
                <option label= "MultiPlug 1" value="MP1"/>
38
            </options>
39
        </param>
40
        <param field="Mode2" label="Folder to store ini files (RM2/RM mini3)" width="300px" required="true" default="C:/BroadlinkRM2"/>
41
        <param field="Mode4" label="Generate import Device (RM2/RM mini3)" width="75px">
42
            <options>
43
                <option label= "False" value="no"/>
44
                <option label= "True" value="yes" default="True"/>
45
            </options>
46
        </param>
47-
    global device, brohost, bromac
47+
        <param field="Mode5" label="Port for HTTP server (RM2/RM mini3)" width="50px" required="true" default="9000"/>
48
        <param field="Mode6" label="Debug" width="75px">
49
            <options>
50-
        device = broadlink.rm(host=(brohost,80), mac=bytearray.fromhex(bromac), devtype=0x279d)
50+
                <option label="True" value="Debug"/>
51
                <option label="False" value="Normal"  default="True" />
52
            </options>
53-
        print( "Connected to Broadlink device.")
53+
        </param>
54
    </params>
55-
        print( "Error Connecting to Broadlink device....")
55+
</plugin>
56-
        sys.exit(2)
56+
"""
57
#
58
# Main Import
59
import Domoticz
60-
path=sys.argv[1]
60+
61
import datetime
62-
if not os.path.exists(path):
62+
import time
63-
    print(' ini file not found: ' + path)
63+
import codecs
64-
    sys.exit(1)
64+
import subprocess
65
import socket
66-
config = configparser.ConfigParser()
66+
67-
config.read(path)
67+
68-
Unit=config.get("DEFAULT", "Unit")
68+
# Required for import: path is OS dependent
69-
brohost=config.get("Device", "host")
69+
# Python framework in Domoticz do not include OS dependent path
70-
bromac=config.get("Device", "mac")
70+
71-
sendCommand = config.get("LearnedCode", Unit)
71+
import site
72-
broadlinkConnect()
72+
73-
send()
73+
74
path=''
75
path=site.getsitepackages()
76
for i in path:
77
    sys.path.append(i)
78
#
79
import broadlink
80
#
81
isConnected = False
82
numberDev = 2
83
bypass = False
84
temp = 0
85
learnedCommand = "None"
86
sendCommand = ""
87
loadedCommand = ""
88
nbUpdate = 1
89
isRunning = False
90
custom = ""
91
clear = False
92
RemoteCommand = ""
93
state = True
94
statemp1 = {}
95
energy = 0
96
97
# Domoticz call back functions
98
#
99
100
# Executed once at HW creation/ update. Can create up to 255 devices.
101
def onStart():
102
    global numberDev, nbUpdate
103
104
    if Parameters["Mode6"] == "Debug":
105
        Domoticz.Debugging(1)
106
107
    if  (Parameters["Mode3"] == 'DIS'):
108
        if ( 1 not in Devices):
109
            Domoticz.Device(Name="Discovery",  Unit=1, Type=17, Image=2, Switchtype=17, Used=1).Create()
110
        if ( 2 not in Devices):
111
            Domoticz.Device(Name="Discovery Info",  Unit=2, TypeName="Text", Used=1).Create()
112
113
    elif (Parameters["Mode3"] == 'RM2T' or Parameters["Mode3"] == 'RM2'):
114
        if ( 1 not in Devices ):
115
            Options =   {   "LevelActions"  :"||||" ,
116
                            "LevelNames"    :"Off|Learn|Test|Save|Reset" ,
117
                            "LevelOffHidden":"true",
118
                            "SelectorStyle" :"0"
119
                         }
120
            Domoticz.Device(Name="Command",  Unit=1, TypeName="Selector Switch", Switchtype=18, Image=12, Options=Options, Used=1).Create()
121
122
        if ( 2 not in Devices and Parameters["Mode3"] == 'RM2T'):
123
            Domoticz.Device(Name="Temp",  Unit=2, TypeName="Temperature", Used=1).Create()
124
125
        if ( 254 not in Devices ):
126
            Domoticz.Device(Name="Remote",  Unit=254, Type=17, Image=2, Switchtype=17, Used=1).Create()
127
128
        if ( 255 not in Devices and Parameters["Mode4"] == 'yes' ):
129
            Options =   {   "LevelActions"  :"||||" ,
130
                            "LevelNames"    :"Off|WebStart|Generate|Import|Clear" ,
131
                            "LevelOffHidden":"true",
132
                            "SelectorStyle" :"0"
133
                         }
134
            Domoticz.Device(Name="Import",  Unit=255, TypeName="Selector Switch", Switchtype=18, Image=12, Options=Options, Used=1).Create()
135
136
    elif  (Parameters["Mode3"] == 'SP1' or Parameters["Mode3"] == 'SP2' or Parameters["Mode3"] == 'SP3S'):
137
        if ( 1 not in Devices ):
138
            Domoticz.Device(Name=str(Parameters["Mode3"]),  Unit=1,TypeName="Switch", Image=1, Used=1).Create()
139
        if Parameters["Mode3"] == 'SP3S':
140
            if ( 2 not in Devices ):
141
                Domoticz.Device(Name=str(Parameters["Mode3"]),  Unit=2,TypeName="Usage", Used=1).Create()
142
            if ( 3 not in Devices ):
143
                Domoticz.Device(Name=str(Parameters["Mode3"]),  Unit=3,TypeName="kWh", Used=1).Create()
144
145
    elif  (Parameters["Mode3"] == 'A1'):
146
        if ( 1 not in Devices ):
147
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - Status',  Unit=1,Type=17, Image=17, Switchtype=17, Used=1).Create()
148
        if ( 2 not in Devices ):
149
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - Temperature',  Unit=2,TypeName="Temperature", Used=1).Create()
150
        if ( 3 not in Devices ):
151
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - Humidity',  Unit=3,TypeName="Humidity", Used=1).Create()
152
        if ( 4 not in Devices ):
153
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - Air Quality',  Unit=4,TypeName="Air Quality", Used=1).Create()
154
        if ( 5 not in Devices ):
155
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - Noise',  Unit=5,TypeName="Sound Level", Used=1).Create()
156
        if ( 6 not in Devices ):
157
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - Light',  Unit=6,TypeName="Illumination", Used=1).Create()
158
159
    elif  (Parameters["Mode3"] == 'MP1'):
160
        if ( 1 not in Devices ):
161
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - Status',  Unit=1,TypeName="Switch",Image=9, Used=1).Create()
162
        if ( 2 not in Devices ):
163
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - plug1',  Unit=2,TypeName="Switch",Image=1, Used=1).Create()
164
        if ( 3 not in Devices ):
165
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - plug2',  Unit=3,TypeName="Switch",Image=1, Used=1).Create()
166
        if ( 4 not in Devices ):
167
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - plug3',  Unit=4,TypeName="Switch",Image=1, Used=1).Create()
168
        if ( 5 not in Devices ):
169
            Domoticz.Device(Name=str(Parameters["Mode3"]) + ' - plug4',  Unit=5,TypeName="Switch",Image=1, Used=1).Create()
170
171
172
    DumpConfigToLog()
173
    Domoticz.Heartbeat(30)
174
175
    numberDev = len(Devices) - 1
176
    if (255 in Devices):
177
        UpdateDevice(255, 0, 'Off')
178
        numberDev = numberDev - 1
179
180
    Domoticz.Log("Connecting to: "+Parameters["Address"]+":"+Parameters["Mode1"])
181
182
    if Parameters["Mode3"] == 'RM2' or Parameters["Mode3"] == 'RM2T' or Parameters["Mode3"] == 'A1':
183
        if not broadlinkConnect():
184
            UpdateDevice(1, 0, 'Off')
185
        else:
186
            UpdateDevice(1, 1, '10')
187
    elif Parameters["Mode3"] != 'DIS':
188
        if checkPower():
189
            if state:
190
                UpdateDevice(1, 1, 'On')
191
#                if (Parameters["Mode3"] == 'MP1'):
192
#                    AllPlugOn()
193
            else:
194
                UpdateDevice(1, 0, 'Off')
195
#                if (Parameters["Mode3"] == 'MP1'):
196
#                    AllPlugOff()
197
        else:
198
            UpdateDevice(1, 0, 'Off')
199
#            if (Parameters["Mode3"] == 'MP1'):
200
#                    AllPlugOff()
201
202
    if Parameters["Mode3"] == 'RM2' or Parameters["Mode3"] == 'RM2T':
203
        if not os.path.exists(Parameters["Mode2"] + "/import"):
204
            os.makedirs(Parameters["Mode2"] + "/import")
205
        if not os.path.exists(Parameters["Mode2"] + "/remote"):
206
            os.makedirs(Parameters["Mode2"] + "/remote")
207
208
        genRemote()
209
210
    Domoticz.Log("Device Number begin to : "+ str(numberDev))
211
212
    return True
213
214
# executed each time we click on device thru domoticz GUI
215
def onCommand(Unit, Command, Level, Hue):
216
    global sendCommand, isRunning, learnedCommand, isConnected
217
218
    Domoticz.Log("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level) + " , Connected : " + str(isConnected))
219
220
    Command = Command.strip()
221
222
    if (Command == 'Set Level'):
223
        if (Parameters["Mode3"] == 'RM2T' or Parameters["Mode3"] == 'RM2'):
224
            if (Unit == 1):  # Command selector
225
                if (Level == 10):
226
                        learn()
227
                if (Level == 20):
228
                    sendCommand = learnedCommand
229
                    if learnedCommand == "None":
230
                        Domoticz.Log('Nothing to send')
231
                    else:
232
                        send()
233
                if (Level == 30):
234
                    if learnedCommand == "None":
235
                        Domoticz.Log('Nothing to save')
236
                    else:
237
                        custom = ""
238
                        if save():
239
                            UpdateDevice(1,1,'10')
240
                            learnedCommand = "None"
241
                if (Level == 40):
242
                    if learnedCommand == "None":
243
                        Domoticz.Log('Nothing to reset')
244
                    else:
245
                        reset()
246
            elif (Unit == 255):
247
                if (Level == 10):
248
                    if startWeb():
249
                        isRunning = True
250
                        UpdateDevice(255,1,'10')
251
                    else:
252
                        UpdateDevice(255,0,'Off')
253
                        Domoticz.Error ('Not able to start Web server')
254
                if (Level == 20):
255
                    if createIniImport():
256
                        UpdateDevice(255,1,'20')
257
                    else:
258
                        UpdateDevice(255,0,'Off')
259
                        Domoticz.Error ('Error with json files to import')
260
                if (Level == 30):
261
                    clear = False
262
                    if manageIniImport(clear):
263
                        UpdateDevice(255,1,'30')
264
                    else:
265
                        UpdateDevice(255,0,'Off')
266
                if (Level == 40):
267
                    clear = True
268
                    if manageIniImport(clear):
269
                        UpdateDevice(255,1,'40')
270
                    else:
271
                        UpdateDevice(255,0,'Off')
272
            else:
273
                Domoticz.Error('Unit unknown')
274
275
    elif (Command == 'On'):
276
277
        if (Unit == 1 and Parameters['Mode3'] == 'DIS'):  # Discovery
278
            if Discover():
279
                UpdateDevice(Unit, 1, 'Found : ' + str(len(brodevices )) + ' device(s)')
280
            else:
281
                Domoticz.Error('Not able to find Broadlink device')
282
        elif (Unit==254 and RemoteCommand != "" ):
283
            UpdateDevice(Unit, 1, 'On')
284
        elif ( Unit==1 and (Parameters['Mode3'] == 'SP1' or Parameters['Mode3'] == 'SP2' or Parameters['Mode3'] == 'SP3S')):
285
            try:
286
                device.set_power(True)
287
                UpdateDevice(Unit,1,'On')
288
            except:
289
                Domoticz.Error(' error to put ON SP1/SP2/SP3 ')
290
                isConnected = False
291
        elif (Parameters['Mode3'] == 'MP1'):
292
            if ( Unit > 1 and Unit < 6):
293
                try:
294
                    device.set_power(Unit - 1, True)
295
                    UpdateDevice(Unit,1,'On')
296
                except:
297
                    Domoticz.Error(' Error to put ON MP1 for plug : ' + str(Unit -1))
298
        else:
299
            if (Parameters["Mode3"] == 'RM2T' or Parameters["Mode3"] == 'RM2'):
300
                genCommand(Unit)
301
            else:
302
                Domoticz.Error('Unknown command')
303
304
    elif (Command == 'Off'):
305
306
        if (Unit == 1 and Parameters['Mode3'] == 'DIS'):  # Discovery
307
                UpdateDevice(Unit, 0, 'Off')
308
        elif ( Unit==1 and (Parameters['Mode3'] == 'SP1' or Parameters['Mode3'] == 'SP2' or Parameters['Mode3'] == 'SP3S')):
309
            try:
310
                device.set_power(False)
311
                UpdateDevice(Unit,0,'Off')
312
            except:
313
                Domoticz.Error(' error to put Off SP1/SP2/SP3 ')
314
                isConnected = False
315
        elif (Parameters['Mode3'] == 'MP1'):
316
            if ( Unit > 1 and Unit < 6):
317
                try:
318
                    device.set_power(Unit - 1, False)
319
                    UpdateDevice(Unit,0,'Off')
320
                except:
321
                    Domoticz.Error(' Error to put Off MP1 for plug : ' + str(Unit -1))
322
        else:
323
            try:
324
                UpdateDevice(Unit, 0, 'Off')
325
            except:
326
                Domoticz.Error('Unit error update')
327
                raise
328
    elif ( Unit == 254):
329
        if remoteSend(Command):
330
            UpdateDevice(Unit, 1, Command)
331
        else:
332
            UpdateDevice(Unit, 1, 'undefined')
333
334
    else:
335
        Domoticz.Error('Unknown command')
336
337
    return True
338
339
# execution depend of Domoticz.Heartbeat(x) x in seconds
340
def onHeartbeat():
341
    global bypass, isConnected, isRunning, state
342
343
    now = datetime.datetime.now()
344
345
    if (255 in Devices and isRunning == True):
346
            isAlive()
347
348
    if bypass is True:
349
        bypass = False
350
        return
351
# for RM2T type we check temp every 2 minutes
352
    if Parameters["Mode3"] == 'RM2T':
353
        if ((now.minute % 2) == 0):
354
            bypass = True
355
            if isConnected:
356
                if checkTemp():
357
                    UpdateDevice(2, 1, temp)
358
                    UpdateDevice(1,1,'10')
359
                else:
360
                    isConnected = False
361
                    UpdateDevice(1,0,'Off')
362
            else:
363
                broadlinkConnect()
364
# for A1 we get sensor data every 30 seconds
365
    elif Parameters["Mode3"] == 'A1':
366
        if Parameters["Mode6"] == "Debug":
367
            Domoticz.Log(" A1 called")
368
        if ((now.minute % 1) == 0):
369
            bypass = False
370
            if isConnected:
371
                if checkSensor():
372
                    UpdateDevice(1, 1, 'Get Data From Sensors')
373
                else:
374
                    isConnected = False
375
            else:
376
                broadlinkConnect()
377
# for SP3S we get energy/status data every 30 seconds
378
    elif Parameters["Mode3"] == 'SP3S':
379
        if Parameters["Mode6"] == "Debug":
380
            Domoticz.Log(" SP3S called")
381
        if ((now.minute % 1) == 0):
382
            bypass = False
383
            if isConnected:
384
                if getEnergy():
385
                    UpdateDevice(2, 0, str(energy))
386
                    UpdateDevice(3, 0, str(energy))
387
                else:
388
                    isConnected = False
389
                if checkPower():
390
                    if state:
391
                        UpdateDevice(1, 1, 'On')
392
                    else:
393
                        UpdateDevice(1, 0, 'Off')
394
                else:
395
                    isConnected = False
396
            else:
397
                broadlinkConnect()
398
# for MP1 we check status every 1 minute
399
#    elif Parameters["Mode3"] == 'MP1':
400
#        if Parameters["Mode6"] == "Debug":
401
#            Domoticz.Log("MP1 called")
402
#        if ((now.minute % 1) == 0):
403
#            bypass = True
404
#            if isConnected:
405
#                if checkPower():
406
#                    if statemp1:
407
#                        UpdateDevice(1, 1, 'On')
408
#                        AllPlugOn()
409
#                    else:
410
#                        UpdateDevice(1, 0, 'Off')
411
#                        AllPlugOff()
412
#                else:
413
#                    isConnected = False
414
#            else:
415
#                broadlinkConnect()
416
# for SP1/2 we check status every 1 minute
417
    elif Parameters["Mode3"] == 'SP1' or Parameters["Mode3"] == 'SP2':
418
        if Parameters["Mode6"] == "Debug":
419
            Domoticz.Log("SP1/SP2 called")
420
        if ((now.minute % 1) == 0):
421
            bypass = True
422
            if isConnected:
423
                if checkPower():
424
                    if state:
425
                        UpdateDevice(1, 1, 'On')
426
                    else:
427
                        UpdateDevice(1, 0, 'Off')
428
                else:
429
                    isConnected = False
430
            else:
431
                broadlinkConnect()
432
# for DIS we do nothing
433
    elif Parameters["Mode3"] == 'DIS':
434
        return
435
# for RM2 we try to connect every 5 minutes if checkTemp is False
436
    else:
437
        if (now.minute % 5 == 0):
438
            if not checkTemp():
439
                UpdateDevice(1,0,'Off')
440
                if broadlinkConnect():
441
                    UpdateDevice(1,1,'10')
442
            bypass = True
443
444
    return True
445
446
# executed once when HW updated/removed
447
def onStop():
448
    Domoticz.Log("onStop called")
449
    return True
450
451
# Generic helper functions
452
def DumpConfigToLog():
453
    for x in Parameters:
454
        if Parameters[x] != "":
455
            Domoticz.Debug( "'" + x + "':'" + str(Parameters[x]) + "'")
456
    Domoticz.Debug("Device count: " + str(len(Devices)))
457
    for x in Devices:
458
        Domoticz.Debug("Device:           " + str(x) + " - " + str(Devices[x]))
459
        Domoticz.Debug("Device ID:       '" + str(Devices[x].ID) + "'")
460
        Domoticz.Debug("Device Name:     '" + Devices[x].Name + "'")
461
        Domoticz.Debug("Device nValue:    " + str(Devices[x].nValue))
462
        Domoticz.Debug("Device sValue:   '" + Devices[x].sValue + "'")
463
        Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
464
    return
465
466
# Update Device into DB
467
def UpdateDevice(Unit, nValue, sValue):
468
    # Make sure that the Domoticz device still exists (they can be deleted) before updating it
469
    if (Unit in Devices):
470
        if Unit == 1:
471
            if (Devices[Unit].nValue != nValue) or (Devices[Unit].sValue != sValue):
472
                Devices[Unit].Update(nValue=nValue, sValue=str(sValue))
473
                Domoticz.Log("Update "+str(nValue)+":'"+str(sValue)+"' ("+Devices[Unit].Name+")")
474
        else:
475
            Devices[Unit].Update(nValue=nValue, sValue=str(sValue))
476
            Domoticz.Log("Update "+str(nValue)+":'"+str(sValue)+"' ("+Devices[Unit].Name+")")
477
    return
478
479
# generate command to execute and update name in ini file if necessary
480
def genCommand(Unit):
481
    global loadedCommand, sendCommand, nbUpdate
482
483
    Domoticz.Log('Generate on Command for learned code stored on unit/ini :' + str(Unit))
484
485
    path=str(Parameters["Mode2"]) + "/" + str(Parameters["Key"]) + "-" + str(Parameters["HardwareID"]) + "-" + str(Unit) + ".ini"
486
487
    if not os.path.exists(path):
488
        Domoticz.Error(' ini file not found: ' + str(path))
489
        return
490
491
    config = configparser.ConfigParser()
492
    config.read(path,encoding='utf8')
493
    loadedCommand = config.get("LearnedCode", str(Unit))
494
    if Parameters["Mode6"] == "Debug":
495
        Domoticz.Log(" Code loaded : " + loadedCommand)
496
    sendCommand = loadedCommand
497
    if isConnected:
498
        send()
499
    if Parameters["Mode6"] == "Debug":
500
        Domoticz.Log(' <b> Command line : ' + '"' + Parameters['HomeFolder'] + 'plugin_send.py' +  '" ' + path + ' </b>')
501
502
    if Unit in Devices:
503
        try:
504
            UpdateDevice(Unit,1,'On-'+str(nbUpdate))
505
            nbUpdate +=1
506
        except:
507
            Domoticz.Error("Not able to update device : " + str(Unit))
508
509
        try:
510
            if not (Devices[Unit].Name == config.get("DEFAULT","customname")):
511
512
                config.set('DEFAULT','customname',Devices[Unit].Name)
513
514
                try:
515
                    with open(path, 'w') as configfile:
516
                        config.write(configfile)
517
                except IOError:
518
                    Domoticz.Error('Error updating config file')
519
                    raise
520
        except:
521
            Domoticz.Error('Error updating config file, customname param missing')
522
            raise
523
524
    return
525
526
# save learned/imported code and create Domoticz device
527
def save():
528
    global path, learnedCommand, Unit, numberDev, custom
529
530
    numberDev +=1
531
    path=str(Parameters["Mode2"]) + "/" + str(Parameters["Key"]) + "-" + str(Parameters["HardwareID"]) + "-" + str(numberDev) + ".ini"
532
533
    if os.path.exists(path):
534
        Domoticz.Error('File exist : ' + path)
535
        return False
536
    else:
537
        try:
538
            create_config(path,str(numberDev),learnedCommand,custom)
539
        except:
540
            Domoticz.Error('Not able to create : ' + path)
541
            return False
542
    try:
543
        Domoticz.Device(Name=str(Parameters["HardwareID"])+"-" + str(numberDev)+ " " + custom,  Unit=numberDev, TypeName="Selector Switch", Type=244, Switchtype=9, Subtype=73).Create()
544
    except:
545
        Domoticz.Error('Not able to create device')
546
        return False
547
548
    if Parameters["Mode6"] == "Debug":
549
        Domoticz.Log(' <b> Command line : ' + '"' + Parameters['HomeFolder'] + 'plugin_send.py' +  '" ' + path + ' </b>')
550
551
    return True
552
553
def reset():
554
    global learnedCommand
555
556
    UpdateDevice(1, 0, 'Off')
557
    learnedCommand = "None"
558
    if Parameters["Mode6"] == "Debug":
559
        Domoticz.Log("Reset learned command")
560
561
    return True
562
563
# discover Broadlink device on the Network
564
def Discover():
565
    global brodevices
566
567
    Domoticz.Log("All plugin system is on pause for 5s...")
568
    brodevices = broadlink.discover(timeout=5)
569
    Domoticz.Log("Found " + str(len(brodevices )) + " broadlink devices")
570
571
    if str(len(brodevices )) == 0:
572
        return False
573
574
    txtdevice = ''
575
576
    for index, item in enumerate(brodevices):
577
578
        brodevices[index].auth()
579
580
        broip = brodevices[index].host
581
        broip = str(broip)
582
        Domoticz.Log( "<b>Device " + str(index + 1) +" Host address = " + broip[1:19] + "</b>")
583
        txtdevice = txtdevice + "[Device " + str(index + 1) +" Host address = " + broip[1:19] + "]"
584
        macadd = ''.join(format(x, '02x') for x in brodevices[index].mac[::-1])
585
        macadd = str(macadd)
586
        brotype = str(brodevices[index].type)
587
        Domoticz.Log( "<b>Device " + str(index + 1) +" MAC address = " + macadd + " Type: " + brotype + "</b>")
588
        txtdevice = txtdevice + "[Device " + str(index + 1) +" MAC address = " + macadd + " Type: " + brotype + "]"
589
        if len(txtdevice) > 200:txtdevice=txtdevice[0:197] + '...'
590
591
    UpdateDevice(2,1,txtdevice)
592
593
    return True
594
595
# Put Broadlink on Learn , packet received converted to Hex
596
def learn():
597
    global learnedCommand
598
599
    if not isConnected:
600
        broadlinkConnect()
601
602
    Domoticz.Log("All plugin system is on pause for 5s...")
603
    Domoticz.Log("When Broadlink led is lit press the button on your remote within 5 seconds")
604
605
    try:
606
        device.enter_learning()
607
    except:
608
        Domoticz.Error ('Not able to learn command')
609
        return False
610
611
    time.sleep(5)
612
613
    ir_packet = device.check_data()
614
    if Parameters["Mode6"] == "Debug":
615
        Domoticz.Log(str(ir_packet))
616
617
    if str(ir_packet) == "None":
618
        Domoticz.Log('Command not received')
619
        learnedCommand= "None"
620
        return False
621
622
    learnedCommand=(codecs.encode(ir_packet, 'hex_codec')).decode('utf-8')
623
    if Parameters["Mode6"] == "Debug":
624
        Domoticz.Log(learnedCommand)
625
626
    Domoticz.Log( "Code stored in memory" )
627
    UpdateDevice(1, 1, '20')
628
629
    return True
630
631
# send Hex command
632
def send():
633
    global sendCommand
634
635
    if not sendCommand:
636
        Domoticz.Error('Nothing to send')
637
        return False
638
639
    sendCommand = bytes.fromhex(sendCommand)
640
    if Parameters["Mode6"] == "Debug":
641
        Domoticz.Log(str(sendCommand))
642
643
    try:
644
        device.send_data(sendCommand)
645
        Domoticz.Log( "Code Sent....")
646
    except:
647
        Domoticz.Error( "Code Sent WARNING....Probably timeout")
648
        return False
649
650
    return True
651
652
#Create a config file
653
def create_config(path,Unit,learnedCommand,custom):
654
655
    config = configparser.ConfigParser()
656
    config['DEFAULT'] = {   'PluginKey'     : Parameters["Key"],
657
                            'PluginName'    : Parameters["Name"],
658
                            'PluginFolder'  : Parameters["HomeFolder"],
659
                            'HardwareID'    : Parameters["HardwareID"],
660
                            'Unit'          : Unit,
661
                            'CustomName'    : custom
662
                        }
663
664
    config['Device'] = {    'Host'  : Parameters["Address"],
665
                            'Mac'   : Parameters["Mode1"]
666
                        }
667
    config['LearnedCode'] = {}
668
    UniteCode = config['LearnedCode']
669
    UniteCode[str(Unit)] = learnedCommand
670
    try:
671
        with open(path, 'w') as configfile:
672
            config.write(configfile)
673
    except IOError:
674
        Domoticz.Error('Error create config file')
675
        raise
676
677
    if Parameters["Mode6"] == "Debug":
678
        Domoticz.Log( "ini file creation...." + path)
679
680
    return
681
682
# connect to Broadlink
683
def broadlinkConnect():
684
    global device, isConnected
685
686
    try:
687
        if (Parameters["Mode3"] == 'RM2T'  or Parameters["Mode3"] == 'RM2'):
688
            device = broadlink.rm(host=(Parameters["Address"],80), mac=bytearray.fromhex(Parameters["Mode1"]), devtype=0x279d)
689
        elif (Parameters["Mode3"] == 'A1'):
690
            device = broadlink.a1(host=(Parameters["Address"],80), mac=bytearray.fromhex(Parameters["Mode1"]), devtype=0x2714)
691
        elif (Parameters["Mode3"] == 'SP1'):
692
            device = broadlink.sp1(host=(Parameters["Address"],80), mac=bytearray.fromhex(Parameters["Mode1"]), devtype=0)
693
        elif (Parameters["Mode3"] == 'SP2' or Parameters["Mode3"] == 'SP3S'):
694
            device = broadlink.sp2(host=(Parameters["Address"],80), mac=bytearray.fromhex(Parameters["Mode1"]), devtype=0x2711)
695
        elif (Parameters["Mode3"] == 'MP1'):
696
            device = broadlink.mp1(host=(Parameters["Address"],80), mac=bytearray.fromhex(Parameters["Mode1"]), devtype=0x4EB5)
697
        else:
698
            device = 'unknown'
699
        device.auth()
700
        device.host
701
        isConnected = True
702
        Domoticz.Log( "Connected to Broadlink device: " + str(Parameters["Address"]))
703
    except:
704
        Domoticz.Error( "Error Connecting to Broadlink device...." + str(Parameters["Address"]))
705
        isConnected = False
706
        return False
707
708
    return True
709
710
# get temperature for RM2T
711
def checkTemp():
712
    global temp
713
714
    try:
715
        temp=device.check_temperature()
716
    except:
717
        Domoticz.Error( "Error getting temperature data from Broadlink device....Timeout")
718
        return False
719
720
    if temp > 60:
721
        return False
722
723
    return True
724
725
# Start Web server for Transfert for RM2/RM2T
726
def startWeb():
727
728
    if sys.platform.startswith('linux'):
729
    # linux specific code here
730
        cmdFile = Parameters["HomeFolder"] + 'plugin_http.sh'
731
    elif sys.platform.startswith('darwin'):
732
    # mac
733
        cmdFile = Parameters["HomeFolder"] + 'plugin_http.sh'
734
    elif sys.platform.startswith('win32'):
735
    #  win specific
736
        cmdFile = '"' + Parameters["HomeFolder"] + 'plugin_http.cmd' + '"'
737
738
    commandtoexecute =  cmdFile + " 0.0.0.0 " + Parameters["Mode5"] + " " + Parameters["Mode2"]
739
740
    try:
741
        subprocess.check_call(commandtoexecute, shell=True, timeout=2)
742
    except subprocess.CalledProcessError as e:
743
        Domoticz.Error(str(e.returncode))
744
        Domoticz.Error(str(e.cmd))
745
        Domoticz.Error(str(e.output))
746
        return False
747
748
    if Parameters["Mode6"] == "Debug":
749
        Domoticz.Log("Subprocess " + commandtoexecute + " launched...")
750
751
    return True
752
753
# check Webserver is running, if not put device Off
754
def isAlive():
755
    global isRunning
756
757
    socket.setdefaulttimeout(2)
758
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
759
760
    try:
761
        s.connect(('127.0.0.1', int(Parameters["Mode5"])))
762
        isRunning = True
763
        s.sendall(b"GET /")
764
    except socket.error as e:
765
        isRunning = False
766
        UpdateDevice(255,0,'Off')
767
768
    s.close()
769
    if Parameters["Mode6"] == "Debug":
770
            Domoticz.Log("Web isAlive status :" +str(isRunning))
771
772
    return
773
774
#import json files and transfrom to ini file and hex imported code for RM2/RM2T
775
def createIniImport():
776
    global learnedCommand, custom
777
778
    import json
779
780
    path=Parameters["Mode2"] + "/import/"
781
782
    try:
783
        with open(path+"jsonSubIr", encoding='utf8') as remote_name:
784
            data_remote = json.load(remote_name)
785
    except ValueError: # includes simplejson.decoder.JSONDecodeError
786
        return False
787
788
    try:
789
        with open(path+"jsonButton", encoding='utf8') as button_name:
790
            data_button = json.load(button_name)
791
    except ValueError: # includes simplejson.decoder.JSONDecodeError
792
        return False
793
794
    try:
795
        with open(path+"jsonIrCode", encoding='utf8') as code_name:
796
            data_code = json.load(code_name)
797
    except ValueError: # includes simplejson.decoder.JSONDecodeError
798
        return False
799
800
    recCode = open(path+"simulate.txt", 'w')
801
    CRLF="\n"
802
803
    for i in range(0, len(data_code)):
804
        button = data_code[i]['buttonId']
805
        for j in range(0, len(data_button)):
806
            if data_button[j]['id'] == button:
807
                numName = data_button[j]['subIRId']
808
                buttonName = data_button[j]['name']
809
                for k in range(0, len(data_remote)):
810
                    if data_remote[k]['id'] == numName:
811
                        name = data_remote[k]['name']
812
                        break
813
                    else:
814
                        name = "unknown"
815
                break
816
            else:
817
                buttonName = "unknown"
818
819
        code = ''.join('%02x' % (i & 0xff) for i in data_code[i]['code'])
820
        result = "Numrec : " + str(i) + " Button number: " + str(button ) + " " + "Number name : " + str(numName) + " Name : " + name + " " + buttonName + " Code : " + str(code)
821
        custom = name + " " + buttonName
822
        path = Parameters["Mode2"] + "/import/" + "IMP-" + str(i) + ".ini"
823
824
        create_config(path,i,code,custom)
825
        recCode.writelines(result+CRLF)
826
827
        if Parameters["Mode6"] == "Debug":
828
            Domoticz.Log(result)
829
830
    filelink = "file://" +  Parameters["Mode2"] + "/import/" + "simulate.txt"
831
    Domoticz.Log("Number of devices to create : " + str( i + 1 ))
832
    Domoticz.Log("You need to select Import for that")
833
    Domoticz.Log('Simulate.txt file has been created with all codes on it. Click <a target="_blank"  href="' + filelink + '" style="color:blue">here</a> to see the path')
834
835
    return True
836
837
# if clear is True we will erase all files, if False we will create devices and erase ini files
838
def manageIniImport(clear):
839
    global custom, learnedCommand
840
841
    import glob
842
    import errno
843
844
    path = Parameters["Mode2"] + "/import/*.ini"
845
    files = glob.glob(path)
846
847
    if not files:
848
        Domoticz.Log("No ini files found")
849
        if clear == False:
850
            return False
851
    else:
852
        for name in files: # 'file' is a builtin type, 'name' is a less-ambiguous variable name.
853
            if clear == False:
854
                try:
855
                    with open(name) as f: # No need to specify 'r': this is the default.
856
                        config = configparser.ConfigParser()
857
                        config.read(name,encoding='utf8')
858
                        UnitNumber=config.get("DEFAULT", "unit")
859
                        custom=config.get("DEFAULT", "customname")
860
                        learnedCommand = config.get("LearnedCode", str(UnitNumber))
861
                        createDev()
862
                except IOError as exc:
863
                    if exc.errno != errno.EISDIR: # Do not fail if a directory is found, just ignore it.
864
                        raise # Propagate other kinds of IOErro
865
            os.remove(name)
866
            if Parameters["Mode6"] == "Debug":
867
                Domoticz.Log(name + "  removed")
868
869
    if clear == True:
870
        path = Parameters["Mode2"] + "/import/json*"
871
        files = glob.glob(path)
872
        if not files:
873
            Domoticz.Log("No json files found")
874
            return False
875
        else:
876
            for name in files:
877
                os.remove(name)
878
                if Parameters["Mode6"] == "Debug":
879
                    Domoticz.Log(name + "  removed")
880
881
    return True
882
883
def createDev():
884
885
    if not save() and numberDev < 254:
886
        createDev()
887
888
    return
889
890
def remoteSend(Command):
891
892
    if Command in remoteKEY:
893
        k = remoteKEY.index(Command)
894
        try:
895
            genCommand(remotetoSEND[k])
896
        except IndexError:
897
            Domoticz.Error('Send error or Remote command not set in ini file: ' + Command)
898
            return False
899
        if Parameters["Mode6"] == "Debug":
900
            Domoticz.Log('Remote send: ' + str(k+1) + " " + str(remotetoSEND[k]))
901
    else:
902
        Domoticz.Error('Remote command not defined: ' + Command)
903
        return False
904
905
    return True
906
907
# get config ini file
908
def get_remoteconfig():
909
    global RemoteCommand
910
911
    name = Parameters["Mode2"] + "/remote/plugin_remote_"+ str(Parameters["HardwareID"]) + ".ini"
912
913
    if os.path.isfile(name):
914
        try:
915
            with open(name) as f: # No need to specify 'r': this is the default.
916
                        config = configparser.ConfigParser()
917
                        config.read(name,encoding='utf8')
918
                        RemoteCommand = config.get("Custom", "Command")
919
        except IOError as exc:
920
            Domoticz.Error('error : ' + str(exc))
921
            raise # Propagate other kinds of IOErro
922
923
        if Parameters["Mode6"] == "Debug":
924
            Domoticz.Log( "ini file read...." + name)
925
            Domoticz.Log( "Custom Commands: " + RemoteCommand)
926
    else:
927
        if Parameters["Mode6"] == "Debug":
928
            Domoticz.Log( "No ini file :" + name)
929
            Domoticz.Log( "Custom Commands for Remote not managed")
930
931
    return
932
933
# generate tuple for remote
934
def genRemote():
935
    global remoteKEY, remotetoSEND
936
937
    from ast import literal_eval as make_tuple
938
939
    get_remoteconfig()
940
941
    if RemoteCommand:
942
        remotetoSEND = make_tuple(RemoteCommand)
943
944
    remoteKEY=( "Home",
945
                "Up",
946
                "Info",
947
                "Left",
948
                "Select",
949
                "Right",
950
                "Back",
951
                "Down",
952
                "ContextMenu",
953
                "ChannelUp",
954
                "FullScreen",
955
                "VolumeUp",
956
                "Channels",
957
                "ShowSubtitles",
958
                "Mute",
959
                "ChannelDown",
960
                "Stop",
961
                "VolumeDown",
962
                "BigStepBack",
963
                "Rewind",
964
                "PlayPause",
965
                "FastForward",
966
                "BigStepForward"
967
                )
968
969
    return
970
971
# retreive sensors data and update devices  for type A1
972
def checkSensor():
973
974
# data
975
    try:
976
        data=device.check_sensors()
977
    except:
978
        Domoticz.Error( "Error getting sensor data from Broadlink device....Timeout")
979
        return False
980
981
    if Parameters["Mode6"] == "Debug":
982
        Domoticz.Log(str(data))
983
984
# raw data
985
    try:
986
        data_raw=device.check_sensors_raw()
987
    except:
988
        Domoticz.Error( "Error getting sensor data raw from Broadlink device....Timeout")
989
        return False
990
991
    if Parameters["Mode6"] == "Debug":
992
        Domoticz.Log(str(data_raw))
993
994
# temperature
995
    tem =  data['temperature']
996
997
    UpdateDevice(2,0,str(tem))
998
999
# humidity
1000
    hum =  data['humidity']
1001
    if hum < 50:
1002
        hum_raw = "0"
1003
    elif hum < 60:
1004
        hum_raw = "1"
1005
    elif hum < 70:
1006
        hum_raw = "2"
1007
    else:
1008
        hum_raw = "3"
1009
1010
    UpdateDevice(3,int(hum),hum_raw)
1011
1012
# air quality
1013
    air =  data['air_quality']
1014
    air_raw = data_raw['air_quality']
1015
    if air_raw == 0:
1016
        air_raw = 400
1017
    elif air_raw == 1:
1018
        air_raw = 800
1019
    elif air_raw == 2:
1020
        air_raw = 1000
1021
    elif air_raw == 3:
1022
        air_raw = 1800
1023
    else:
1024
        air_raw = 2800
1025
1026
    UpdateDevice(4,int(air_raw),str(air))
1027
1028
# noise
1029
    noi =  data['noise']
1030
    noi_raw = data_raw['noise']
1031
    if noi_raw == 0:
1032
        noi_raw = 20
1033
    elif noi_raw == 1:
1034
        noi_raw = 60
1035
    elif noi_raw == 2:
1036
        noi_raw = 80
1037
    else:
1038
        noi_raw = 120
1039
1040
    UpdateDevice(5,0,int(noi_raw))
1041
1042
# illimunation
1043
    lux =  data['light']
1044
    lux_raw = data_raw['light']
1045
    if lux_raw == 0:
1046
        lux_raw = 10
1047
    elif lux_raw == 1:
1048
        lux_raw = 200
1049
    elif lux_raw == 2:
1050
        lux_raw = 400
1051
    elif lux_raw == 3:
1052
        lux_raw = 800
1053
    else:
1054
        lux_raw = 1600
1055
1056
    UpdateDevice(6,0,int(lux_raw))
1057
1058
1059
    return True
1060
1061
# SP1/2/3 devices
1062
def checkPower():
1063
    global state
1064
1065
    try:
1066
        state = device.check_power()
1067
        if Parameters["Mode6"] == "Debug":
1068
            Domoticz.Log( ' Power state : ' + str(state))
1069
    except:
1070
        Domoticz.Error ('check power error')
1071
        return False
1072
1073
    return True
1074
1075
# MP1 Device
1076
def checkPowerMP1():
1077
    global statemp1
1078
1079
    statemp1 = {}
1080
1081
    try:
1082
        statemp1 = device.check_power()
1083
        if Parameters["Mode6"] == "Debug":
1084
            Domoticz.Log( ' Power state : ' + str(statemp1))
1085
    except:
1086
        Domoticz.Error ('MP1 check power error')
1087
        return False
1088
1089
    return True
1090
1091
# SP3s device (SP2)
1092
def getEnergy():
1093
    global energy
1094
1095
    try:
1096
        energy = device.get_energy()
1097
        if Parameters["Mode6"] == "Debug":
1098
            Domoticz.Log( ' Energy : ' + str(energy))
1099
    except:
1100
        Domoticz.Error ('Get Energy error')
1101
        return False
1102
1103
    return True
1104
1105
# we put all switchs of the MP1 device to Off
1106
def AllPlugOff():
1107
1108
    UpdateDevice (2,0,'Off')
1109
    UpdateDevice (3,0,'Off')
1110
    UpdateDevice (4,0,'Off')
1111
    UpdateDevice (5,0,'Off')
1112
1113
    return
1114
1115
# we put all switchs of the MP1 device to On
1116
def AllPlugOn():
1117
1118
    UpdateDevice (2,1,'On')
1119
    UpdateDevice (3,1,'On')
1120
    UpdateDevice (4,1,'On')
1121
    UpdateDevice (5,1,'On')
1122
1123
    return