Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ds18b20 sensor #42

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions mudpi/extensions/ds18b20/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
DS18B20 Extension
Includes sensor interface for DS18B20.
Works for Dallas 1-wire temperature sensors.
"""
from mudpi.extensions import BaseExtension


class Extension(BaseExtension):
namespace = 'ds18b20'
update_interval = 60

41 changes: 41 additions & 0 deletions mudpi/extensions/ds18b20/docs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# DS18B20
The `ds18b20` extension connects to a DS18B20 device to gather temperature readings. The sensor will return `temperature`.

This extension does not take an extension level configs and is focused on [interfaces.]({{url('docs/developers-interfaces')}})

---
<div class="mb-2"></div>

## Sensor Interface
Provides a [sensor]({{url('docs/sensors')}}) that returns a DS18B20 readings.

<table class="mt-2 mb-4">
<thead><tr><td width="15%">Option</td><td width="15%">Type</td><td width="15%">Required</td><td width="55%">Description</td></tr></thead>
<tbody>
<tr><td class="font-600">key</td><td class="text-italic text-sm">[String]</td><td>Yes</td><td class="text-xs">Unique slug id for the component</td></tr>
<tr><td class="font-600">name</td><td class="text-italic text-sm">[String]</td><td>No</td><td class="text-xs">Friendly display name of component. Useful for UI.</td></tr>
<tr><td class="font-600">one_wire_ID</td><td class="text-italic text-sm">[String]</td><td>No</td><td class="text-xs">The address of the DS18B20 sensor. This should be set if using mulitple DS18B20 sensors and can be found as a directory in `/sys/bus/w1/devices/` in the format of `28-XXXXXXXXXXXXX` . <strong>Default 10</strong></td></tr>
</tbody>
</table>

### Config Examples
Here is a config of a complete example sensor.

```json
"sensor": [{
"key": "ds18b20_outside",
"interface": "ds18b20",
"name": "Outside temperature",
"one_wire_ID": "28-0000000000001"
}]
```

### Data
Here is an example of the data returned by the DS18B20:

```json
{
"temperature": 50
}
```
---
9 changes: 9 additions & 0 deletions mudpi/extensions/ds18b20/extension.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "DS18B20 1-Wire Temperature Sensor",
"namespace": "ds18b20",
"details": {
"description": "Provides interface for ds18b20 sensors to take tempurature readings.",
"documentation": "https://mudpi.app/docs/sensors/ds18b20"
},
"requirements": ["glob2", "python-time"]
}
133 changes: 133 additions & 0 deletions mudpi/extensions/ds18b20/sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"""
DS18B20 Sensor Interface
Connects to a DS18B20 device to get
temperature readings.
"""
import os
import glob
import time

from mudpi.constants import METRIC_SYSTEM
from mudpi.extensions import BaseInterface
from mudpi.extensions.sensor import Sensor
from mudpi.logger.Logger import Logger, LOG_LEVEL
from mudpi.exceptions import MudPiError, ConfigError

os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')


#device_folder = '/sys/bus/w1/devices/28-XXXXXXXXXXXXX'
#device_folder = glob.glob(base_dir + '/28*')[0]
#device_file = device_folder + '/w1_slave'

class Interface(BaseInterface):

def load(self, config):
""" Load DS18B20 sensor component from configs """
sensor = ds18b20(self.mudpi, config)
self.add_component(sensor)
return True

def validate(self, config):
if not isinstance(config, list):
config = [config]

for conf in config:
""" See if 1-wire ID was passed by the user in the config file """
if not conf.get('one_wire_ID'):
Logger.log(
LOG_LEVEL["debug"],
'DS18B20 one_wire_ID not set. Will search for device.'
)

return config

class ds18b20(Sensor):
""" DS18B20 Sensor get readings temperature. """

""" Properties """
@property
def id(self):
""" Return a unique id for the component """
return self.config['key']

@property
def name(self):
""" Return the display name of the component """
return self.config.get('name') or f"{self.id.replace('_', ' ').title()}"

@property
def state(self):
""" Return the state of the component (from memory, no IO!) """
return self._state

@property
def classifier(self):
""" Classification further describing it, effects the data formatting """
return 'temperature'

@property
def one_wire_ID(self):
return self.config['one_wire_ID']


""" Methods """
def init(self):
"""To support multiple 1-wire devices check to see if the ID is set in the config.
If the ID is not set, there should only be a single 28-xxxxxxxxx directory in the base directory, so we use that. """

base_dir = '/sys/bus/w1/devices'

if self.config.get('one_wire_ID') and os.path.isdir(base_dir + '/' + self.config.get('one_wire_ID')):
self.device_file = base_dir + '/' + self.config.get('one_wire_ID') + '/w1_slave'
Logger.log(
LOG_LEVEL["debug"],
'Setting device file to ' + self.device_file
)
else:
Logger.log(
LOG_LEVEL["debug"],
'DS18B20 one_wire_ID not set or not found.'
)
""" Make sure 1-wire device directory exists """
try:
device_folder = glob.glob(base_dir + '/28*')[0]
except:
Logger.log(
LOG_LEVEL["error"],
'Failed to find 1-wire device directory. Ensure device is connected and one_wire_ID corret..'
)
else:
self.device_file = device_folder + '/w1_slave'
return True

def update(self):

def read_temp_raw():
f = open(self.device_file, 'r')
lines = f.readlines()
f.close()
return lines

lines = read_temp_raw()
while lines[0].strip()[-3:] != 'YES':
time.sleep(0.2)
lines = read_temp_raw()
equals_pos = lines[1].find('t=')
if equals_pos != -1:
temp_string = lines[1][equals_pos+2:]
temperature_c = float(temp_string) / 1000.0
temperature_f = temperature_c * 9.0 / 5.0 + 32.0
_temperature = round(temperature_c if self.mudpi.unit_system == METRIC_SYSTEM else temperature_f, 2)
readings = {
'temperature': _temperature,
}
self._state = readings
return readings
else:
Logger.log(
LOG_LEVEL["error"],
'Failed to get reading [DS18B20]. Try again!'
)
return None