Last active
November 5, 2017 16:22
-
-
Save tjeastman/bce98c5c1688fc833475 to your computer and use it in GitHub Desktop.
read temperature values from a OneWire device via owserver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
# install dependencies: | |
# apt-get install libow-dev | |
# pip install onewire influxdb | |
import argparse | |
from datetime import datetime | |
import logging | |
import signal | |
import sys | |
from threading import Timer | |
from time import time | |
from onewire import Onewire | |
from influxdb import client as influxdb | |
def int_signal_handler(signal, frame): | |
logging.info("interrupt detected") | |
sys.exit(0) | |
signal.signal(signal.SIGINT, int_signal_handler) | |
class ServiceLocation(object): | |
def __init__(self, hostname, port=None): | |
self.hostname = hostname | |
self.port = port | |
class ServiceLocationAction(argparse.Action): | |
def __init__(self, option_strings, dest, nargs=None, **kwargs): | |
if nargs is not None and nargs != 1: | |
raise ValueError("Only one argument is allowed for a service location.") | |
super(ServiceLocationAction, self).__init__(option_strings, dest, **kwargs) | |
def __call__(self, parser, namespace, values, option_string=None): | |
if ":" in values: | |
hostname, port = values.split(":", 1) | |
port = int(port) | |
setattr(namespace, self.dest, ServiceLocation(hostname, port)) | |
else: | |
hostname = values | |
setattr(namespace, self.dest, ServiceLocation(hostname)) | |
class InfluxDatabaseWriter(object): | |
def __init__(self, location, name): | |
self._client = influxdb.InfluxDBClient(location.hostname, location.port) | |
self._client.switch_database(name) | |
def write(self, datum): | |
self._client.write_points([{ | |
"measurement": datum.measurement, | |
"tags": {}, | |
"time": datum.time.isoformat(), | |
"fields": {"value": datum.value}}]) | |
class OneWireTemperatureSensor(object): | |
def __init__(self, location, identifier): | |
ow = Onewire("{hostname:s}:{port:d}".format( | |
hostname=location.hostname, port=location.port)) | |
self._sensor = ow.sensor(identifier) | |
@property | |
def type(self): | |
return self._sensor.sensor_type | |
@property | |
def identifier(self): | |
return self._sensor.path | |
@property | |
def temperature(self): | |
temperature = self._sensor.read("temperature") | |
if temperature: | |
return float(temperature) | |
else: | |
return None | |
class TimeSeriesDatum(object): | |
def __init__(self, measurement, value): | |
self.time = datetime.utcnow() | |
self.measurement = measurement | |
self.value = value | |
class Repeater(object): | |
def __init__(self, interval, function, *args): | |
self._interval = interval | |
self._function = function | |
self._arguments = args | |
def __call__(self): | |
self._start_timer() | |
self._call_function() | |
def _call_function(self): | |
self._function(*self._arguments) | |
def _start_timer(self): | |
# align the execution of the function with the next multiple of | |
# the delay interval from the original start time | |
interval = self._interval | |
start_seconds = self._start_seconds | |
delay_seconds = interval - ((time() - start_seconds) % interval) | |
Timer(delay_seconds, self).start() | |
def start(self): | |
self._start_seconds = time() | |
self._start_timer() | |
self._call_function() | |
def read_temperature(sensor, database): | |
temperature = sensor.temperature | |
if temperature is not None: | |
datum = TimeSeriesDatum("temp", temperature) | |
database.write(datum) | |
logging.info("temperature is {value:f} °C [{time!s}]".format( | |
value=datum.value, time=datum.time)) | |
else: | |
logging.warning("could not convert temperature \"{value:s}\"".format( | |
value=temperature)) | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser( | |
description="read temperatures from a OneWire sensor") | |
parser.add_argument("device", type=str, help="device ID") | |
parser.add_argument("-I", "--influx", type=str, default="localhost", | |
help="InfluxDB hostname (default: %(default)s", | |
action=ServiceLocationAction), | |
parser.add_argument("-D", "--database", type=str, default=None, | |
help="InfluxDB database name"), | |
parser.add_argument("-H", "--host", type=str, default="localhost:4304", | |
help="owserver hostname (default: %(default)s)", | |
action=ServiceLocationAction), | |
parser.add_argument("-d", "--delay", type=int, default=1, | |
help="delay between sensor readings in seconds (default: %(default)d)") | |
args = parser.parse_args() | |
logging.basicConfig(level=logging.INFO) | |
database = InfluxDatabaseWriter(args.influx, args.database) | |
logging.info("connected to InfluxDB server at {hostname:s}:{port:d}".format( | |
hostname=args.influx.hostname, port=args.influx.port)) | |
sensor = OneWireTemperatureSensor(args.host, args.device) | |
logging.info("connected to OneWire server at {hostname:s}:{port:d}".format( | |
hostname=args.host.hostname, port=args.host.port)) | |
logging.info("found device {identifier:s} (type = {type:s})".format( | |
identifier=sensor.identifier, type=sensor.type)) | |
Repeater(args.delay, read_temperature, sensor, database).start() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Just what I was looking for so I can graph temperatures in the house as part of my underfloor heating controls. I have been playing with the code for a while, it does most of what I want except I have been struggling to pass tags to influx. I want a human readable tag per sensor, so I can get multiple sensors into one database. For example the one wire sensor 28.438128060000 could have a human readable alias of "kitchen" and when using Grafana it will be easy to configure the graphs. I have tried adding another argument to the parser but could not get my head around the coding to include it in the influx write. I haven't done any coding for a number of years and OO coding was never my strong point but I am trying. Any help appreciated.