-
Notifications
You must be signed in to change notification settings - Fork 5
/
plugin.py
295 lines (229 loc) · 9.46 KB
/
plugin.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
"""
<plugin key="Mi_flower_mate" name="Xiaomi Mi Flower Mate" author="blauwebuis" version="1.0.0" wikilink="https://www.domoticz.com/wiki/Plugins/Mi_flower_mate" externallink="https://www.domoticz.com/forum/viewtopic.php?f=65&t=22281">
<description>
This plugin connects to Mi Flower Mate flower sensors over Bluetooth LE. It requires the BluePy library to be installed on the system.
</description>
<params>
<param field="Mode1" label="Device selection" width="300px" required="true">
<options>
<option label="Automatic scanning" value="auto" default="true"/>
<option label="Manual selection (add below)" value="manual"/>
</options>
</param>
<param field="Mode2" label="Devices mac adresses, capitalised and comma separated" width="300px" required="false" default=""/>
</params>
</plugin>
"""
bluepyError = 0
try:
import Domoticz
except ImportError:
import fakeDomoticz as Domoticz
import time
import sys
import shelve
import os
from miflora import miflora_scanner, BluepyBackend
import miflora
try:
from miflora.miflora_poller import MiFloraPoller, \
MI_CONDUCTIVITY, MI_MOISTURE, MI_LIGHT, MI_TEMPERATURE, MI_BATTERY
except:
bluepyError = 1
try:
from miflora.backends.bluepy import BluepyBackend
except:
bluepyError = 1
class BasePlugin:
def __init__(self):
self.macs = []
self.currentlyPolling = 100
return
def onStart(self):
#Domoticz.Debugging(1)
sys.path.append("/usr/local/lib/python3.4/dist-packages")
sys.path.append("/usr/local/lib/python3.5/dist-packages")
if bluepyError == 1:
Domoticz.Error("Error loading Flora libraries")
Domoticz.Debug("Mi Flora - devices made so far (max 255): " + str(len(Devices)))
# create custom icons
if "Mi-Flower-Mate" not in Images:
Domoticz.Log("Creating custom icon for the master Mi Flower Mate poll switch")
Domoticz.Image(Filename="Mi-Flower-Mate.zip").Create()
if ("Mi-Flower-Mate-Light" not in Images):
Domoticz.Log("Creating custom icon for the master Mi Flower Mate light switch")
Domoticz.Image(Filename="Mi-Flower-Mate-Light.zip").Create()
if ("Mi-Flower-Mate-Moist" not in Images):
Domoticz.Log("Creating custom icon for the master Mi Flower Mate moisture switch")
Domoticz.Image(Filename="Mi-Flower-Mate-Moist.zip").Create()
if ("Mi-Flower-Mate-Temp" not in Images):
Domoticz.Log("Creating custom icon for the master Mi Flower Mate temperature switch")
Domoticz.Image(Filename="Mi-Flower-Mate-Temp.zip").Create()
if ("Mi-Flower-Mate-NPK" not in Images):
Domoticz.Log("Creating custom icon for the master Mi Flower Mate conductivity switch")
Domoticz.Image(Filename="Mi-Flower-Mate-NPK.zip").Create()
# create master toggle switch
if 1 not in Devices:
Domoticz.Device(Name="update Mi Flowermates", Unit=1, Type=17, Switchtype=9, Image=Images['Mi-Flower-Mate'].ID, Used=1).Create()
# get the mac addresses of the sensors
if Parameters["Mode1"] == 'auto':
Domoticz.Log("Automatic mode is selected")
self.floraScan()
else:
Domoticz.Log("Manual mode is selected")
self.macs = parseCSV(Parameters["Mode2"])
self.createSensors()
#Domoticz.Log("macs = {}".format(self.macs))
#Devices[Unit].Update(nValue=1,sValue="Off")
def onStop(self):
Domoticz.Log("onStop called")
def onConnect(self, Connection, Status, Description):
Domoticz.Log("onConnect called")
def onMessage(self, Connection, Data, Status, Extra):
Domoticz.Log("onMessage called")
def onCommand(self, Unit, Command, Level, Hue):
#Domoticz.Log("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
Domoticz.Log("amount of Flower Mates to now ask for data: " + str(len(self.macs)) )
# flip the switch icon, and then get the plant data.
if Unit == 1:
#Devices[Unit].Update(nValue=1,sValue="Off") # best to always turn it off I guess.
#By setting this to 0, the polling function will run.
self.currentlyPolling = 0
def onHeartbeat(self):
# for now this uses the shelve database as its source of truth.
if( self.currentlyPolling < len(self.macs) ):
try:
self.getPlantData(int(self.currentlyPolling))
if( self.currentlyPolling == len(self.macs) - 1 ):
Devices[1].Update(nValue=0,sValue="Off")
except:
pass
self.currentlyPolling = self.currentlyPolling + 1
# function to create corresponding sensors in Domoticz if there are Mi Flower Mates which don't have them yet.
def createSensors(self):
# create the domoticz sensors if necessary
if ((len(Devices) - 1)/4) < len(self.macs):
Domoticz.Debug("Creating new sensors")
# Create the sensors. Later we get the data.
for idx, mac in enumerate(self.macs):
Domoticz.Debug("Creating new sensors for Mi Flower Mate at "+str(mac))
sensorBaseName = "#" + str(idx) + " "
#moisture
sensorNumber = (idx*4) + 2
if sensorNumber not in Devices:
sensorName = sensorBaseName + "Moisture"
Domoticz.Debug("Creating first sensor, #"+str(sensorNumber))
Domoticz.Debug("Creating first sensor, name: "+str(sensorName))
Domoticz.Device(Name=sensorName, Unit=sensorNumber, TypeName="Percentage", Image=Images['Mi-Flower-Mate-Moist'].ID, Used=1).Create()
Domoticz.Log("Created device: "+Devices[sensorNumber].Name)
#temperature
sensorNumber = (idx*4) + 3
sensorName = sensorBaseName + "Temperature"
Domoticz.Device(Name=sensorName, Unit=sensorNumber, TypeName="Temperature", Image=Images['Mi-Flower-Mate-Temp'].ID, Used=1).Create()
Domoticz.Log("Created device: "+Devices[sensorNumber].Name)
#light
sensorNumber = (idx*4) + 4
sensorName = sensorBaseName + "Light"
Domoticz.Device(Name=sensorName, Unit=sensorNumber, TypeName="Illumination", Image=Images['Mi-Flower-Mate-Light'].ID, Used=1).Create()
Domoticz.Log("Created device: "+Devices[sensorNumber].Name)
#fertility
sensorNumber = (idx*4) + 5
sensorName = sensorBaseName + "Conductivity"
Domoticz.Device(Name=sensorName, Unit=sensorNumber, TypeName="Custom", Used=1,Subtype=31, Image=Images['Mi-Flower-Mate-NPK'].ID, Options={"Custom":"1;µS/cm"}).Create()
Domoticz.Log("Created device: "+Devices[sensorNumber].Name)
# function to poll a Flower Mate for its data
def getPlantData(self, idx):
#for idx, mac in enumerate(self.macs):
mac = self.macs[idx]
Domoticz.Log("getting data from sensor: "+str(mac))
try:
poller = MiFloraPoller(str(mac), BluepyBackend)
Domoticz.Debug("Firmware: {}".format(poller.firmware_version()))
val_bat = int("{}".format(poller.parameter_value(MI_BATTERY)))
nValue = 0
except:
Domoticz.Log("poller error")
#moisture
sensorNumber1 = (idx*4) + 2
try:
val_moist = "{}".format(poller.parameter_value(MI_MOISTURE))
Devices[sensorNumber1].Update(nValue=int(val_moist), sValue=val_moist, BatteryLevel=val_bat)
Domoticz.Log("moisture = " + str(val_moist))
except:
Domoticz.Log("error getting moisture data")
#temperature
sensorNumber2 = (idx*4) + 3
try:
val_temp = "{}".format(poller.parameter_value(MI_TEMPERATURE))
Devices[sensorNumber2].Update(nValue=nValue, sValue=val_temp, BatteryLevel=val_bat)
Domoticz.Log("temperature = " + str(val_temp))
except:
Domoticz.Log("error getting temperature data")
#light
sensorNumber3 = (idx*4) + 4
try:
val_lux = "{}".format(poller.parameter_value(MI_LIGHT))
Devices[sensorNumber3].Update(nValue=nValue, sValue=val_lux, BatteryLevel=val_bat)
Domoticz.Log("light = " + str(val_lux))
except:
Domoticz.Log("error getting light data")
#fertility
sensorNumber4 = (idx*4) + 5
try:
val_cond = "{}".format(poller.parameter_value(MI_CONDUCTIVITY))
Devices[sensorNumber4].Update(nValue=nValue, sValue=val_cond, BatteryLevel=val_bat)
Domoticz.Log("conductivity = " + str(val_cond))
except:
Domoticz.Log("Error getting conductivity data")
# function to scan for devices, and store and compare the outcome
def floraScan(self):
Domoticz.Log("Scanning for Mi Flower Mate sensors")
#databaseFile=os.path.join(os.environ['HOME'],'XiaomiMiFlowerMates')
# first, let's get the list of devices we already know about
database = shelve.open('XiaomiMiMates')
try:
knownSensors = database['macs']
oldLength = len(knownSensors)
Domoticz.Debug("Already know something:" + str(oldLength))
Domoticz.Log("Already known devices:" + str(knownSensors))
except:
knownSensors = []
database['macs'] = knownSensors
oldLength = 0;
Domoticz.Debug("No existing sensors in system?")
#Next we scan to look for new sensors
try:
foundFloras = miflora_scanner.scan(BluepyBackend, 3)
Domoticz.Log("Number of devices found via bluetooth scan = " + str(len(foundFloras)))
except:
foundFloras = []
Domoticz.Log("Scan failed")
for sensor in foundFloras:
if sensor not in knownSensors:
knownSensors.append(str(sensor))
Domoticz.Log("Found new device: " + str(sensor))
if len(knownSensors) != oldLength:
database['macs'] = knownSensors
Domoticz.Log("Updating database")
database.close()
self.macs = knownSensors
self.createSensors()
global _plugin
_plugin = BasePlugin()
def onStart():
global _plugin
_plugin.onStart()
def onStop():
global _plugin
_plugin.onStop()
def onCommand(Unit, Command, Level, Hue):
global _plugin
_plugin.onCommand(Unit, Command, Level, Hue)
def onHeartbeat():
global _plugin
_plugin.onHeartbeat()
def parseCSV(strCSV):
listvals = []
for value in strCSV.split(","):
listvals.append(value)
return listvals