Skip to content

Commit

Permalink
Added plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
ashwinscale committed Mar 15, 2018
1 parent 7cfead0 commit 9bbf917
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 2 deletions.
2 changes: 0 additions & 2 deletions README.md

This file was deleted.

15 changes: 15 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
******************************************************************************
FogLAMP South MAX31865 Async mode Plugin
******************************************************************************

This directory contains a plugin that receives readings from AM2315
humidity/temperature sensor.

Requirements:
sudo apt-get install python3-smbus

=============
Since the plugin runs in a separate process and its shutdown is controlled by the
central FogLAMP server, pressing CTRL-C does not terminate the process properly.


220 changes: 220 additions & 0 deletions am2315async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# -*- coding: utf-8 -*-

# FOGLAMP_BEGIN
# See: http://foglamp.readthedocs.io/
# FOGLAMP_END

""" Module for AM2315 'async' type plugin """

import asyncio
import copy
import datetime
import json
import uuid

from foglamp.common import logger
from foglamp.plugins.common import utils
from foglamp.services.south import exceptions
from foglamp.services.south.ingest import Ingest
import smbus

__author__ = "Ashwin Gopalakrishnan"
__copyright__ = "Copyright (c) 2017 OSIsoft, LLC"
__license__ = "Apache 2.0"
__version__ = "${VERSION}"


_DEFAULT_CONFIG = {
'plugin': {
'description': 'AM2315 async plugin',
'type': 'string',
'default': 'am2315async'
},
'shutdownThreshold': {
'description': 'Time in seconds allowed for shutdown to complete the pending tasks',
'type': 'integer',
'default': '10'
}
}

_LOGGER = logger.setup(__name__, level=20)


def plugin_info():
""" Returns information about the plugin.
Args:
Returns:
dict: plugin information
Raises:
"""

return {
'name': 'AM2315 Async plugin',
'version': '1.0',
'mode': 'async',
'type': 'south',
'interface': '1.0',
'config': _DEFAULT_CONFIG
}


def plugin_init(config):
""" Initialise the plugin.
Args:
config: JSON configuration document for the South device configuration category
Returns:
handle: JSON object to be used in future calls to the plugin
Raises:
"""
data = copy.deepcopy(config)

bus = smbus.SMBus(1)
_LOGGER.info('AM2315 initialized')
return bus


def plugin_start(bus):
""" Extracts data from the sensor and returns it in a JSON document as a Python dict.
Available for async mode only.
Args:
handle: handle returned by the plugin initialisation call
Returns:
returns a sensor reading in a JSON document, as a Python dict, if it is available
None - If no reading is available
Raises:
TimeoutError
"""
sensor_add = 0x5C
start_add = 0x00
function_code = 0x03
register_number = 0x04
response_bytes = 8
attempt_threshold = 50
async def save_data():
attempt = 1
if bus is None:
return
try:
while True:
await asyncio.sleep(1)
try:
try:
# wake up call
bus.write_i2c_block_data(sensor_add, function_code, [start_add, register_number])
except Exception as e:
# expected exception as sensor is sleeping
pass
# request data
bus.write_i2c_block_data(sensor_add, function_code, [start_add, register_number])
# read data
sensor_response = bytearray(bus.read_i2c_block_data(sensor_add, function_code, response_bytes))
# function_code=sensor_response[0]
# bytes_returned=sensor_response[1]
# relative_humidity_high=sensor_response[2]
# relative_humidity_low=sensor_response[3]
# temperature_high=sensor_response[4]
# temperature_low=sensor_response[5]
# crc_low=sensor_response[6]
# crc_high=sensor_response[7]

# temperature
temperature= (sensor_response[4] * 256 + sensor_response[5])/10
# humidity
humidity= (sensor_response[2] * 256 + sensor_response[3])/10
# crc
crc = sensor_response[7] * 256 + sensor_response[6]
# calc crc to verify
calc_crc = 0xFFFF
for byte in sensor_response[0:6]:
calc_crc = calc_crc ^ byte
for i in range(1,9):
if(calc_crc & 0x01):
calc_crc = calc_crc >> 1
calc_crc = calc_crc ^ 0xA001
else:
calc_crc = calc_crc >> 1

if calc_crc != crc:
pass

time_stamp = str(datetime.datetime.now(tz=datetime.timezone.utc))
data = {
'asset': 'temperature',
'timestamp': time_stamp,
'key': str(uuid.uuid4()),
'readings': {
"temperature": temperature,
}
}
await Ingest.add_readings(asset='AM2315/{}'.format(data['asset']),
timestamp=data['timestamp'], key=data['key'],
readings=data['readings'])
data = {
'asset': 'humidity',
'timestamp': time_stamp,
'key': str(uuid.uuid4()),
'readings': {
"humidity": humidity,
}
}
await Ingest.add_readings(asset='AM2315/{}'.format(data['asset']),
timestamp=data['timestamp'], key=data['key'],
readings=data['readings'])
attempt = 1
except (Exception) as ex:
attempt += 1
if attempt > attempt_threshold:
raise RuntimeError("Attempt {} exceeds attempt threshold of {}".format(attempt, attempt_threshold))
except (Exception, RuntimeError, pexpect.exceptions.TIMEOUT) as ex:
_LOGGER.exception("AM2315 async exception: {}".format(str(ex)))
raise exceptions.DataRetrievalError(ex)
_LOGGER.debug("AM2315 async reading: {}".format(json.dumps(data)))
return
asyncio.ensure_future(save_data())


def plugin_reconfigure(handle, new_config):
""" Reconfigures the plugin
it should be called when the configuration of the plugin is changed during the operation of the South device service;
The new configuration category should be passed.
Args:
handle: handle returned by the plugin initialisation call
new_config: JSON object representing the new configuration category for the category
Returns:
new_handle: new handle to be used in the future calls
Raises:
"""
_LOGGER.info("Old config for AM2315 plugin {} \n new config {}".format(handle, new_config))

# Find diff between old config and new config
diff = utils.get_diff(handle, new_config)

# TODO
new_handle = copy.deepcopy(new_config)
new_handle['restart'] = 'no'
return new_handle


def _plugin_stop(handle):
""" Stops the plugin doing required cleanup, to be called prior to the South device service being shut down.
Args:
handle: handle returned by the plugin initialisation call
Returns:
Raises:
"""
_LOGGER.info('AM2315 (async) Disconnected.')


def plugin_shutdown(handle):
""" Shutdowns the plugin doing required cleanup, to be called prior to the South device service being shut down.
Args:
handle: handle returned by the plugin initialisation call
Returns:
Raises:
"""
_plugin_stop(handle)
_LOGGER.info('AM2315 async plugin shut down.')
25 changes: 25 additions & 0 deletions cmds.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--- Database commands to insert the new plugin into FogLAMP

--- Create the south service instannce
INSERT INTO foglamp.scheduled_processes ( name, script ) VALUES ( 'AM2315', '["services/south"]' );

--- Add the schedule to start the service at system startup
INSERT INTO foglamp.schedules ( id, schedule_name, process_name, schedule_type,
schedule_time, schedule_interval, exclusive, enabled )
VALUES ( '56450027-824e-417b-a5d3-0f9a4211aaa5', -- id
'AM2315 async south', -- schedule_name
'AM2315', -- process_name
1, -- schedule_type (startup)
NULL, -- schedule_time
'00:00:00', -- schedule_interval
true, -- exclusive
true -- enabled
);

--- Insert the config needed to load the plugin
INSERT INTO foglamp.configuration ( key, description, value )
VALUES ( 'AM2315',
'AM2315 async South Plugin',
' { "plugin" : { "type" : "string", "value" : "am2315async", "default" : "am2315async", "description" : "Python module name of the plugin to load" } } '
);

0 comments on commit 9bbf917

Please sign in to comment.