Skip to content

Commit c89c53c

Browse files
authored
Merge branch 'development' into feature/plugins
2 parents b678903 + 1932049 commit c89c53c

File tree

6 files changed

+77
-36
lines changed

6 files changed

+77
-36
lines changed

myDevices/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""
22
This package contains the Cayenne agent, which is a full featured client for the Cayenne IoT project builder: https://cayenne.mydevices.com. It sends system information as well as sensor and actuator data and responds to actuator messages initiated from the Cayenne dashboard and mobile apps.
33
"""
4-
__version__ = '2.0.2'
4+
__version__ = '2.0.3'
5+

myDevices/devices/digital/gpio.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
class NativeGPIO(Singleton, GPIOPort):
4141
IN = 0
4242
OUT = 1
43+
OUT_LOW = 2
44+
OUT_HIGH = 3
4345

4446
ASUS_GPIO = 44
4547

@@ -310,11 +312,9 @@ def __setFunction__(self, channel, value):
310312
self.checkDigitalChannelExported(channel)
311313
self.checkPostingFunctionAllowed()
312314
try:
313-
if value == self.IN:
314-
value = 'in'
315-
else:
316-
value = 'out'
317-
try:
315+
value_dict = {self.IN: 'in', self.OUT: 'out', self.OUT_LOW: 'low', self.OUT_HIGH: 'high'}
316+
value = value_dict[value]
317+
try:
318318
self.functionFile[channel].write(value)
319319
self.functionFile[channel].seek(0)
320320
except:

myDevices/devices/digital/helper.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,17 @@ def __str__(self):
7171
return "MotionSensor"
7272

7373
class DigitalActuator(DigitalSensor):
74-
def __init__(self, gpio, channel, invert=False):
74+
def __init__(self, gpio, channel, invert=False, last_state=None):
7575
DigitalSensor.__init__(self, gpio, channel, invert)
76-
self.gpio.setFunction(self.channel, GPIO.OUT)
76+
function = GPIO.OUT
77+
if gpio == 'GPIO' and last_state is not None:
78+
if self.invert:
79+
last_state = int(not last_state)
80+
if last_state == 1:
81+
function = GPIO.OUT_HIGH
82+
elif last_state == 0:
83+
function = GPIO.OUT_LOW
84+
self.gpio.setFunction(self.channel, function)
7785

7886
def __str__(self):
7987
return "DigitalActuator"
@@ -90,30 +98,29 @@ def write(self, value):
9098
return self.read()
9199

92100
class LightSwitch(DigitalActuator):
93-
def __init__(self, gpio, channel, invert=False):
94-
DigitalActuator.__init__(self, gpio, channel, invert)
101+
def __init__(self, gpio, channel, invert=False, last_state=None):
102+
DigitalActuator.__init__(self, gpio, channel, invert, last_state)
95103

96104
def __str__(self):
97105
return "LightSwitch"
98106

99107
class MotorSwitch(DigitalActuator):
100-
def __init__(self, gpio, channel, invert=False):
101-
DigitalActuator.__init__(self, gpio, channel, invert)
108+
def __init__(self, gpio, channel, invert=False, last_state=None):
109+
DigitalActuator.__init__(self, gpio, channel, invert, last_state)
102110

103111
def __str__(self):
104112
return "MotorSwitch"
105113

106114
class RelaySwitch(DigitalActuator):
107-
def __init__(self, gpio, channel, invert=False):
108-
DigitalActuator.__init__(self, gpio, channel, invert)
115+
def __init__(self, gpio, channel, invert=False, last_state=None):
116+
DigitalActuator.__init__(self, gpio, channel, invert, last_state)
109117

110118
def __str__(self):
111119
return "RelaySwitch"
112120

113121
class ValveSwitch(DigitalActuator):
114-
def __init__(self, gpio, channel, invert=False):
115-
DigitalActuator.__init__(self, gpio, channel, invert)
122+
def __init__(self, gpio, channel, invert=False, last_state=None):
123+
DigitalActuator.__init__(self, gpio, channel, invert, last_state)
116124

117125
def __str__(self):
118126
return "ValveSwitch"
119-

myDevices/devices/manager.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,20 @@
88
from myDevices.utils.config import Config
99
from myDevices.devices import serial, digital, analog, sensor
1010
from myDevices.devices.instance import DEVICES
11-
from myDevices.devices.onewire import detectOneWireDevices
11+
from myDevices.devices.onewire import detectOneWireDevices, deviceExists, FAMILIES
1212

1313
PACKAGES = [serial, digital, analog, sensor]
1414
DYNAMIC_DEVICES = {}
1515
DEVICES_JSON_FILE = "/etc/myDevices/devices.json"
1616

1717
mutex = RLock()
1818

19+
def missingOneWireDevice(device):
20+
if device['class'] in FAMILIES.values() and ('slave' not in device['args'] or not deviceExists(device['args']['slave'])):
21+
logger.info('1-wire device does not exist: {}, {}'.format(device['class'], device['args']['slave']))
22+
return True
23+
return False
24+
1925
def deviceDetector():
2026
logger.debug('deviceDetector')
2127
try:
@@ -30,6 +36,9 @@ def deviceDetector():
3036
if not found:
3137
if addDevice(dev['name'], dev['device'], dev['description'], dev['args'], "auto") > 0:
3238
saveDevice(dev['name'], int(time()))
39+
missing = [key for key, value in DEVICES.items() if missingOneWireDevice(value)]
40+
for dev in missing:
41+
removeDevice(dev)
3342
except Exception as e:
3443
logger.error("Device detector: %s" % e)
3544

@@ -49,7 +58,7 @@ def findDeviceClass(name):
4958
return getattr(module, name)
5059
return None
5160

52-
def saveDevice(name, install_date):
61+
def saveDevice(name, install_date=None):
5362
with mutex:
5463
logger.debug('saveDevice: ' + str(name))
5564
if name not in DEVICES:
@@ -58,7 +67,8 @@ def saveDevice(name, install_date):
5867
if DEVICES[name]['origin'] == 'manual':
5968
return
6069
DYNAMIC_DEVICES[name] = DEVICES[name]
61-
DEVICES[name]['install_date'] = install_date
70+
if install_date:
71+
DEVICES[name]['install_date'] = install_date
6272
json_devices = getJSON(DYNAMIC_DEVICES)
6373
with open(DEVICES_JSON_FILE, 'w') as outfile:
6474
outfile.write(json_devices)
@@ -129,12 +139,27 @@ def updateDevice(name, json):
129139

130140
return (c, d, t)
131141

142+
def updateDeviceState(name, value):
143+
with mutex:
144+
try:
145+
if not name in DEVICES:
146+
return
147+
device = DEVICES[name]
148+
if 'last_state' not in device['args'] or device['args']['last_state'] != value:
149+
logger.info('Saving state {} for device {}'.format(value, name))
150+
device['args'].update({'last_state': value})
151+
saveDevice(name)
152+
except:
153+
pass
154+
132155
def addDevice(name, device, description, args, origin):
133156
with mutex:
134157
if name in DEVICES:
135158
logger.error("Device <%s> already exists" % name)
136159
return -1
137160
logger.debug('addDevice: ' + str(name) + ' ' + str(device))
161+
if missingOneWireDevice({'class': device, 'args': args}):
162+
return -1
138163
# if '/' in device:
139164
# deviceClass = device.split('/')[0]
140165
# else:

myDevices/devices/onewire.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,5 @@ def detectOneWireDevices():
105105
debug('Error detecting 1-wire devices: {}'.format(err))
106106
return devices
107107

108+
def deviceExists(slave):
109+
return os.path.exists("/sys/bus/w1/devices/%s" % slave)

myDevices/sensors/sensors.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -302,10 +302,10 @@ def SensorsInfo(self):
302302
'DigitalActuator': {'function': 'read', 'data_args': {'type': 'digital_actuator', 'unit': 'd'}},
303303
'AnalogSensor': {'function': 'readFloat', 'data_args': {'type': 'analog_sensor'}},
304304
'AnalogActuator': {'function': 'readFloat', 'data_args': {'type': 'analog_actuator'}}}
305-
extension_types = {'ADC': {'function': 'analogReadAllFloat'},
306-
'DAC': {'function': 'analogReadAllFloat'},
307-
'PWM': {'function': 'pwmWildcard'},
308-
'GPIOPort': {'function': 'wildcard'}}
305+
# extension_types = {'ADC': {'function': 'analogReadAllFloat'},
306+
# 'DAC': {'function': 'analogReadAllFloat'},
307+
# 'PWM': {'function': 'pwmWildcard'},
308+
# 'GPIOPort': {'function': 'wildcard'}}
309309
for device_type in device['type']:
310310
try:
311311
display_name = device['description']
@@ -319,19 +319,22 @@ def SensorsInfo(self):
319319
channel = '{}:{}'.format(device['name'], device_type.lower())
320320
else:
321321
channel = device['name']
322-
cayennemqtt.DataChannel.add(sensors_info, cayennemqtt.DEV_SENSOR, channel, value=self.CallDeviceFunction(func), name=display_name, **sensor_type['data_args'])
322+
value = self.CallDeviceFunction(func)
323+
cayennemqtt.DataChannel.add(sensors_info, cayennemqtt.DEV_SENSOR, channel, value=value, name=display_name, **sensor_type['data_args'])
324+
if 'DigitalActuator' == device_type and value in (0, 1):
325+
manager.updateDeviceState(device['name'], value)
323326
except:
324327
exception('Failed to get sensor data: {} {}'.format(device_type, device['name']))
325-
else:
326-
try:
327-
extension_type = extension_types[device_type]
328-
func = getattr(sensor, extension_type['function'])
329-
values = self.CallDeviceFunction(func)
330-
for pin, value in values.items():
331-
cayennemqtt.DataChannel.add(sensors_info, cayennemqtt.DEV_SENSOR, device['name'] + ':' + str(pin), cayennemqtt.VALUE, value, name=display_name)
332-
except:
333-
exception('Failed to get extension data: {} {}'.format(device_type, device['name']))
334-
logJson('Sensors info: {}'.format(sensors_info))
328+
# else:
329+
# try:
330+
# extension_type = extension_types[device_type]
331+
# func = getattr(sensor, extension_type['function'])
332+
# values = self.CallDeviceFunction(func)
333+
# for pin, value in values.items():
334+
# cayennemqtt.DataChannel.add(sensors_info, cayennemqtt.DEV_SENSOR, device['name'] + ':' + str(pin), cayennemqtt.VALUE, value, name=display_name)
335+
# except:
336+
# exception('Failed to get extension data: {} {}'.format(device_type, device['name']))
337+
info('Sensors info: {}'.format(sensors_info))
335338
return sensors_info
336339

337340
def AddSensor(self, name, description, device, args):
@@ -523,13 +526,16 @@ def SensorCommand(self, command, sensorId, channel, value):
523526
info('Sensor not found')
524527
return result
525528
if command in commands:
526-
info('Sensor found: {}'.format(instance.DEVICES[sensorId]))
529+
device = instance.DEVICES[sensorId]
530+
info('Sensor found: {}'.format(device))
527531
func = getattr(sensor, commands[command]['function'])
528532
value = commands[command]['value_type'](value)
529533
if channel:
530534
result = self.CallDeviceFunction(func, int(channel), value)
531535
else:
532536
result = self.CallDeviceFunction(func, value)
537+
if 'DigitalActuator' in device['type']:
538+
manager.updateDeviceState(sensorId, value)
533539
return result
534540
warn('Command not implemented: {}'.format(command))
535541
return result

0 commit comments

Comments
 (0)