diff --git a/debian/control b/debian/control index e34a7229..9aec7a97 100644 --- a/debian/control +++ b/debian/control @@ -13,6 +13,7 @@ Build-depends: libboblight-dev, python:any, qtbase5-dev, qtconnectivity5-dev, + libow-dev, Standards-Version: 3.9.3 @@ -420,6 +421,22 @@ Description: nymea.io plugin for networkdetector This package will install the nymea.io plugin for networkdetector + +Package: nymea-plugin-onewire +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + nymea-plugins-translations, +Description: nymea.io plugin for one wire devices + The nymea daemon is a plugin based IoT (Internet of Things) server. The + server works like a translator for devices, things and services and + allows them to interact. + With the powerful rule engine you are able to connect any device available + in the system and create individual scenes and behaviors for your environment. + . + This package will install the nymea.io plugin for one wire devices + + Package: nymea-plugin-openweathermap Architecture: any Depends: ${shlibs:Depends}, @@ -833,6 +850,7 @@ Depends: nymea-plugin-boblight, nymea-plugin-unipi, nymea-plugin-serialportcommander, nymea-plugin-systemmonitor, + nymea-plugin-onewire, Replaces: guh-plugins-maker Description: Plugins for nymea IoT server - Meta package for makers, tinkers and hackers The nymea daemon is a plugin based IoT (Internet of Things) server. The diff --git a/debian/nymea-plugin-onewire.install.in b/debian/nymea-plugin-onewire.install.in new file mode 100644 index 00000000..7e326cc2 --- /dev/null +++ b/debian/nymea-plugin-onewire.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginonewire.so diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 82a638c5..2783d989 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -29,6 +29,7 @@ PLUGIN_DIRS = \ mqttclient \ netatmo \ networkdetector \ + onewire \ openweathermap \ osdomotics \ philipshue \ diff --git a/onewire/README.md b/onewire/README.md new file mode 100644 index 00000000..c022e19d --- /dev/null +++ b/onewire/README.md @@ -0,0 +1,39 @@ +# One wire + +This plugin allows to add one wire devices through the one wire file system. + + +## One wire interface device + +This device initializes OWFS, during the device setup you can set OWFS init arguments. +Default arguments are "--i2c=ALL:ALL" to scan for one-wire devices on all I2C interfaces. + +You can simulate one-wire device with following init argument: "--fake=10,22,28,05" + +More about init arguments here: https://www.owfs.org + +## Supported one-wire devices + +* Family Code 10 - Temperature Sensors +..* DS18S20 +..* DS1820 +..* DS18S20-PAR +..* DS1920 +* Family Code 22 - Temperature Sensors +..* DS1822 +..* DS1822-PAR +* Family Code 28 - Temperature Sensors +..* DS18B20 +..* DS18B20-PAR +..* DS18B20X +* Family Code 3B - Temperature Sensors +..* DS1825 +* Family Code 05 - Single channel switch +..* DS2405 +* Family Code 12 - Dual channel switch +..* DS2406 +..* DS2407 +* Family Code 3A - Dual channel switch +..* DS2413 +* Family Code 29 - Eight channel switch +..* DS2408 diff --git a/onewire/devicepluginonewire.cpp b/onewire/devicepluginonewire.cpp new file mode 100644 index 00000000..5db3b7a8 --- /dev/null +++ b/onewire/devicepluginonewire.cpp @@ -0,0 +1,374 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Bernhard Trinnes * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + + +#include "devicepluginonewire.h" +#include "devices/device.h" +#include "plugininfo.h" + +#include +#include + +DevicePluginOneWire::DevicePluginOneWire() +{ +} + +Device::DeviceError DevicePluginOneWire::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) +{ + Q_UNUSED(params); + + if (deviceClassId == temperatureSensorDeviceClassId || + deviceClassId == singleChannelSwitchDeviceClassId || + deviceClassId == dualChannelSwitchDeviceClassId || + deviceClassId == eightChannelSwitchDeviceClassId) { + + if (myDevices().filterByDeviceClassId(oneWireInterfaceDeviceClassId).isEmpty()) { + //No one wire interface intitialized + return Device::DeviceErrorHardwareNotAvailable; + } + + foreach(Device *parentDevice, myDevices().filterByDeviceClassId(oneWireInterfaceDeviceClassId)) { + if (parentDevice->stateValue(oneWireInterfaceAutoAddStateTypeId).toBool()) { + //devices cannot be discovered since auto mode is enabled + return Device::DeviceErrorNoError; + } else { + if (m_oneWireInterface) + m_oneWireInterface->discoverDevices(); + } + } + return Device::DeviceErrorAsync; + } + return Device::DeviceErrorDeviceClassNotFound; +} + + +Device::DeviceSetupStatus DevicePluginOneWire::setupDevice(Device *device) +{ + if (device->deviceClassId() == oneWireInterfaceDeviceClassId) { + qCDebug(dcOneWire) << "Setup one wire interface"; + + if (m_oneWireInterface) { + qCWarning(dcOneWire) << "One wire interface already set up"; + return Device::DeviceSetupStatusFailure; + } + m_oneWireInterface = new OneWire(this); + QByteArray initArguments = device->paramValue(oneWireInterfaceDeviceInitArgsParamTypeId).toByteArray(); + + if (!m_oneWireInterface->init(initArguments)){ + m_oneWireInterface->deleteLater(); + m_oneWireInterface = nullptr; + return Device::DeviceSetupStatusFailure; + } + connect(m_oneWireInterface, &OneWire::devicesDiscovered, this, &DevicePluginOneWire::onOneWireDevicesDiscovered); + return Device::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == temperatureSensorDeviceClassId) { + + qCDebug(dcOneWire) << "Setup one wire temperature sensor" << device->params(); + if (!m_oneWireInterface) { //in case the child was setup before the interface + double temperature = m_oneWireInterface->getTemperature(device->paramValue(temperatureSensorDeviceAddressParamTypeId).toByteArray()); + device->setStateValue(temperatureSensorTemperatureStateTypeId, temperature); + } + return Device::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == singleChannelSwitchDeviceClassId) { + qCDebug(dcOneWire) << "Setup one wire switch" << device->params(); + if (!m_oneWireInterface) { + QByteArray address = device->paramValue(singleChannelSwitchDeviceAddressParamTypeId).toByteArray(); + device->setStateValue(singleChannelSwitchDigitalOutputStateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_A)); + } + return Device::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == dualChannelSwitchDeviceClassId) { + qCDebug(dcOneWire) << "Setup one wire dual switch" << device->params(); + if (!m_oneWireInterface) { + QByteArray address = device->paramValue(dualChannelSwitchDeviceAddressParamTypeId).toByteArray(); + device->setStateValue(dualChannelSwitchDigitalOutput1StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_A)); + device->setStateValue(dualChannelSwitchDigitalOutput2StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_B)); + } + return Device::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == eightChannelSwitchDeviceClassId) { + qCDebug(dcOneWire) << "Setup one wire eight channel switch" << device->params(); + if (!m_oneWireInterface) { + QByteArray address = device->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toByteArray(); + device->setStateValue(eightChannelSwitchDigitalOutput1StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_A)); + device->setStateValue(eightChannelSwitchDigitalOutput2StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_B)); + device->setStateValue(eightChannelSwitchDigitalOutput3StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_C)); + device->setStateValue(eightChannelSwitchDigitalOutput4StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_D)); + device->setStateValue(eightChannelSwitchDigitalOutput5StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_E)); + device->setStateValue(eightChannelSwitchDigitalOutput6StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_F)); + device->setStateValue(eightChannelSwitchDigitalOutput7StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_G)); + device->setStateValue(eightChannelSwitchDigitalOutput8StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_H)); + } + return Device::DeviceSetupStatusSuccess; + } + return Device::DeviceSetupStatusFailure; +} + +void DevicePluginOneWire::postSetupDevice(Device *device) +{ + Q_UNUSED(device); + + if(!m_pluginTimer) { + m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); + connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginOneWire::onPluginTimer); + } +} + +Device::DeviceError DevicePluginOneWire::executeAction(Device *device, const Action &action) +{ + if (device->deviceClassId() == oneWireInterfaceDeviceClassId) { + if (action.actionTypeId() == oneWireInterfaceAutoAddActionTypeId){ + device->setStateValue(oneWireInterfaceAutoAddStateTypeId, action.param(oneWireInterfaceAutoAddActionAutoAddParamTypeId).value()); + return Device::DeviceErrorNoError; + } + return Device::DeviceErrorActionTypeNotFound; + } + + if (device->deviceClassId() == singleChannelSwitchDeviceClassId) { + if (action.actionTypeId() == singleChannelSwitchDigitalOutputActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(singleChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_A, action.param(singleChannelSwitchDigitalOutputActionDigitalOutputParamTypeId).value().toBool()); + + return Device::DeviceErrorNoError; + } + return Device::DeviceErrorActionTypeNotFound; + } + + if (device->deviceClassId() == dualChannelSwitchDeviceClassId) { + if (action.actionTypeId() == dualChannelSwitchDigitalOutput1ActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(dualChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_A, action.param(dualChannelSwitchDigitalOutput1ActionDigitalOutput1ParamTypeId).value().toBool()); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == dualChannelSwitchDigitalOutput2ActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(dualChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_B, action.param(dualChannelSwitchDigitalOutput2ActionDigitalOutput2ParamTypeId).value().toBool()); + return Device::DeviceErrorNoError; + } + return Device::DeviceErrorActionTypeNotFound; + } + + if (device->deviceClassId() == eightChannelSwitchDeviceClassId) { + if (action.actionTypeId() == eightChannelSwitchDigitalOutput1ActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_A, action.param(eightChannelSwitchDigitalOutput1ActionDigitalOutput1ParamTypeId).value().toBool()); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == eightChannelSwitchDigitalOutput2ActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_B, action.param(eightChannelSwitchDigitalOutput2ActionDigitalOutput2ParamTypeId).value().toBool()); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == eightChannelSwitchDigitalOutput3ActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_C, action.param(eightChannelSwitchDigitalOutput3ActionDigitalOutput3ParamTypeId).value().toBool()); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == eightChannelSwitchDigitalOutput4ActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_D, action.param(eightChannelSwitchDigitalOutput4ActionDigitalOutput4ParamTypeId).value().toBool()); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == eightChannelSwitchDigitalOutput5ActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_E, action.param(eightChannelSwitchDigitalOutput5ActionDigitalOutput5ParamTypeId).value().toBool()); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == eightChannelSwitchDigitalOutput6ActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_F, action.param(eightChannelSwitchDigitalOutput6ActionDigitalOutput6ParamTypeId).value().toBool()); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == eightChannelSwitchDigitalOutput7ActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_G, action.param(eightChannelSwitchDigitalOutput7ActionDigitalOutput7ParamTypeId).value().toBool()); + return Device::DeviceErrorNoError; + } + if (action.actionTypeId() == eightChannelSwitchDigitalOutput8ActionTypeId){ + m_oneWireInterface->setSwitchOutput(device->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toByteArray(), OneWire::SwitchChannel::PIO_H, action.param(eightChannelSwitchDigitalOutput8ActionDigitalOutput8ParamTypeId).value().toBool()); + return Device::DeviceErrorNoError; + } + return Device::DeviceErrorActionTypeNotFound; + } + return Device::DeviceErrorNoError; +} + + +void DevicePluginOneWire::deviceRemoved(Device *device) +{ + if (device->deviceClassId() == oneWireInterfaceDeviceClassId) { + m_oneWireInterface->deleteLater(); + m_oneWireInterface = nullptr; + return; + } + + if (myDevices().empty()) { + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); + m_pluginTimer = nullptr; + } +} + + +void DevicePluginOneWire::onPluginTimer() +{ + foreach (Device *device, myDevices()) { + if (device->deviceClassId() == oneWireInterfaceDeviceClassId) { + device->setStateValue(oneWireInterfaceConnectedStateTypeId, m_oneWireInterface->interfaceIsAvailable()); + + if (device->stateValue(oneWireInterfaceAutoAddStateTypeId).toBool()) { + m_oneWireInterface->discoverDevices(); + } + } + + if (device->deviceClassId() == temperatureSensorDeviceClassId) { + QByteArray address = device->paramValue(temperatureSensorDeviceAddressParamTypeId).toByteArray(); + + double temperature = m_oneWireInterface->getTemperature(address); + device->setStateValue(temperatureSensorTemperatureStateTypeId, temperature); + } + + if (device->deviceClassId() == singleChannelSwitchDeviceClassId) { + QByteArray address = device->paramValue(singleChannelSwitchDeviceAddressParamTypeId).toByteArray(); + device->setStateValue(singleChannelSwitchDigitalOutputStateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_A)); + } + + if (device->deviceClassId() == dualChannelSwitchDeviceClassId) { + QByteArray address = device->paramValue(dualChannelSwitchDeviceAddressParamTypeId).toByteArray(); + device->setStateValue(dualChannelSwitchDigitalOutput1StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_A)); + device->setStateValue(dualChannelSwitchDigitalOutput2StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_B)); + } + + if (device->deviceClassId() == eightChannelSwitchDeviceClassId) { + QByteArray address = device->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toByteArray(); + device->setStateValue(eightChannelSwitchDigitalOutput1StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_A)); + device->setStateValue(eightChannelSwitchDigitalOutput2StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_B)); + device->setStateValue(eightChannelSwitchDigitalOutput3StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_C)); + device->setStateValue(eightChannelSwitchDigitalOutput4StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_D)); + device->setStateValue(eightChannelSwitchDigitalOutput5StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_E)); + device->setStateValue(eightChannelSwitchDigitalOutput6StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_F)); + device->setStateValue(eightChannelSwitchDigitalOutput7StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_G)); + device->setStateValue(eightChannelSwitchDigitalOutput8StateTypeId, m_oneWireInterface->getSwitchOutput(address, OneWire::SwitchChannel::PIO_H)); + } + } +} + +void DevicePluginOneWire::onOneWireDevicesDiscovered(QList oneWireDevices) +{ + foreach(Device *parentDevice, myDevices().filterByDeviceClassId(oneWireInterfaceDeviceClassId)) { + + bool autoDiscoverEnabled = parentDevice->stateValue(oneWireInterfaceAutoAddStateTypeId).toBool(); + QList temperatureDeviceDescriptors; + QList singleChannelSwitchDeviceDescriptors; + QList dualChannelSwitchDeviceDescriptors; + QList eightChannelSwitchDeviceDescriptors; + foreach (OneWire::OneWireDevice oneWireDevice, oneWireDevices){ + switch (oneWireDevice.family) { + //https://github.com/owfs/owfs-doc/wiki/1Wire-Device-List + case 0x10: //DS18S20 + case 0x22: //DS1822 + case 0x28: //DS18B20 + case 0x3b: {//DS1825, MAX31826, MAX31850 + DeviceDescriptor descriptor(temperatureSensorDeviceClassId, oneWireDevice.type, "One wire temperature sensor", parentDevice->id()); + ParamList params; + params.append(Param(temperatureSensorDeviceAddressParamTypeId, oneWireDevice.address)); + params.append(Param(temperatureSensorDeviceTypeParamTypeId, oneWireDevice.type)); + foreach (Device *existingDevice, myDevices().filterByDeviceClassId(temperatureSensorDeviceClassId)){ + if (existingDevice->paramValue(temperatureSensorDeviceAddressParamTypeId).toString() == oneWireDevice.address) { + descriptor.setDeviceId(existingDevice->id()); + break; + } + } + descriptor.setParams(params); + temperatureDeviceDescriptors.append(descriptor); + break; + } + case 0x05: { //single channel switch + DeviceDescriptor descriptor(singleChannelSwitchDeviceClassId, oneWireDevice.type, "One wire single channel switch", parentDevice->id()); + ParamList params; + params.append(Param(singleChannelSwitchDeviceAddressParamTypeId, oneWireDevice.address)); + params.append(Param(singleChannelSwitchDeviceTypeParamTypeId, oneWireDevice.type)); + foreach (Device *existingDevice, myDevices().filterByDeviceClassId(singleChannelSwitchDeviceClassId)){ + if (existingDevice->paramValue(singleChannelSwitchDeviceAddressParamTypeId).toString() == oneWireDevice.address) { + descriptor.setDeviceId(existingDevice->id()); + break; + } + } + descriptor.setParams(params); + singleChannelSwitchDeviceDescriptors.append(descriptor); + break; + } + case 0x12: + case 0x3a: {//dual channel switch + DeviceDescriptor descriptor(dualChannelSwitchDeviceClassId, oneWireDevice.type, "One wire dual channel switch", parentDevice->id()); + ParamList params; + params.append(Param(dualChannelSwitchDeviceAddressParamTypeId, oneWireDevice.address)); + params.append(Param(dualChannelSwitchDeviceTypeParamTypeId, oneWireDevice.type)); + foreach (Device *existingDevice, myDevices().filterByDeviceClassId(dualChannelSwitchDeviceClassId)){ + if (existingDevice->paramValue(dualChannelSwitchDeviceAddressParamTypeId).toString() == oneWireDevice.address) { + descriptor.setDeviceId(existingDevice->id()); + break; + } + } + descriptor.setParams(params); + dualChannelSwitchDeviceDescriptors.append(descriptor); + break; + } + case 0x29: { //eight channel switch + DeviceDescriptor descriptor(eightChannelSwitchDeviceClassId, oneWireDevice.type, "One wire eight channel switch", parentDevice->id()); + ParamList params; + params.append(Param(eightChannelSwitchDeviceAddressParamTypeId, oneWireDevice.address)); + params.append(Param(eightChannelSwitchDeviceTypeParamTypeId, oneWireDevice.type)); + foreach (Device *existingDevice, myDevices().filterByDeviceClassId(eightChannelSwitchDeviceClassId)){ + if (existingDevice->paramValue(eightChannelSwitchDeviceAddressParamTypeId).toString() == oneWireDevice.address) { + descriptor.setDeviceId(existingDevice->id()); + break; + } + } + descriptor.setParams(params); + eightChannelSwitchDeviceDescriptors.append(descriptor); + break; + } + default: + qDebug(dcOneWire()) << "Unknown Device discovered" << oneWireDevice.type << oneWireDevice.address; + break; + + } + } + if (autoDiscoverEnabled) { + if (!temperatureDeviceDescriptors.isEmpty()) + emit autoDevicesAppeared(temperatureSensorDeviceClassId, temperatureDeviceDescriptors); + if (!singleChannelSwitchDeviceDescriptors.isEmpty()) + emit autoDevicesAppeared(singleChannelSwitchDeviceClassId, singleChannelSwitchDeviceDescriptors); + if (!dualChannelSwitchDeviceDescriptors.isEmpty()) + emit autoDevicesAppeared(dualChannelSwitchDeviceClassId, dualChannelSwitchDeviceDescriptors); + if (!eightChannelSwitchDeviceDescriptors.isEmpty()) + emit autoDevicesAppeared(eightChannelSwitchDeviceClassId, eightChannelSwitchDeviceDescriptors); + } else { + if (!temperatureDeviceDescriptors.isEmpty()) + emit devicesDiscovered(temperatureSensorDeviceClassId, temperatureDeviceDescriptors); + if (!singleChannelSwitchDeviceDescriptors.isEmpty()) + emit devicesDiscovered(singleChannelSwitchDeviceClassId, singleChannelSwitchDeviceDescriptors); + if (!dualChannelSwitchDeviceDescriptors.isEmpty()) + emit devicesDiscovered(dualChannelSwitchDeviceClassId, dualChannelSwitchDeviceDescriptors); + if (!eightChannelSwitchDeviceDescriptors.isEmpty()) + emit devicesDiscovered(eightChannelSwitchDeviceClassId, eightChannelSwitchDeviceDescriptors); + } + break; + } +} diff --git a/onewire/devicepluginonewire.h b/onewire/devicepluginonewire.h new file mode 100644 index 00000000..16da52e6 --- /dev/null +++ b/onewire/devicepluginonewire.h @@ -0,0 +1,56 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Bernhard Trinnes * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINONEWIRE_H +#define DEVICEPLUGINONEWIRE_H + +#include "plugintimer.h" +#include "devices/deviceplugin.h" +#include "onewire.h" + +#include + +class DevicePluginOneWire : public DevicePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginonewire.json") + Q_INTERFACES(DevicePlugin) + +public: + explicit DevicePluginOneWire(); + + Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; + Device::DeviceSetupStatus setupDevice(Device *device) override; + void postSetupDevice(Device *device) override; + Device::DeviceError executeAction(Device *device, const Action &action) override; + void deviceRemoved(Device *device) override; + +private: + PluginTimer *m_pluginTimer = nullptr; + OneWire *m_oneWireInterface = nullptr; + +private slots: + void onPluginTimer(); + void onOneWireDevicesDiscovered(QList devices); +}; + +#endif // DEVICEPLUGINONEWIRE_H diff --git a/onewire/devicepluginonewire.json b/onewire/devicepluginonewire.json new file mode 100644 index 00000000..67a02047 --- /dev/null +++ b/onewire/devicepluginonewire.json @@ -0,0 +1,274 @@ +{ + "displayName": "One Wire", + "name": "OneWire", + "id": "2c697fb7-0645-466d-9cb9-aa1922c85bee", + "vendors": [ + { + "displayName": "One wire", + "name": "oneWire", + "id": "cecc5fae-29cf-40c0-b1f8-0af2dc8e8a63", + "deviceClasses": [ + { + "id": "c36c68d9-6182-4ae1-972d-b8b5e0cf185f", + "name": "oneWireInterface", + "displayName": "One wire interface", + "interfaces": ["gateway"], + "createMethods": ["user"], + "paramTypes": [ + { + "id": "a0e773ff-fd19-499e-96f0-830168229cd3", + "name": "initArgs", + "displayName": "OWFS init arguments", + "type": "QString", + "defaultValue": "--i2c=ALL:ALL" + } + ], + "stateTypes": [ + { + "id": "d0ded173-c382-4ee3-8e24-3647b4e16afa", + "name": "connected", + "displayName": "connected", + "displayNameEvent": "connected changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "64baf50e-8ed4-4526-8b92-7e4662d6fa39", + "name": "autoAdd", + "displayName": "Auto add one wire devices", + "displayNameAction": "Set auto add mode", + "displayNameEvent": "Auto add one wire devices changed", + "defaultValue": false, + "type": "bool", + "writable": true + } + ] + }, + { + "id": "e13beb24-953c-48b3-9262-7cde31d42ef5", + "name": "temperatureSensor", + "displayName": "Temperature Sensor", + "interfaces": ["temperaturesensor"], + "createMethods": ["discovery"], + "paramTypes": [ + { + "id": "b4368f34-d9bb-496f-84ba-091bd4b6a332", + "name": "address", + "displayName": "Address", + "type": "QString", + "readOnly": true + }, + { + "id": "5005822d-6a32-4bb8-9b77-f79da7382f76", + "name": "type", + "displayName": "Type", + "type": "QString", + "inputType": "TextLine", + "readOnly": true + } + ], + "stateTypes": [ + { + "id": "b04ee2a5-9b27-4ffc-9e12-7e05f5a41690", + "name": "temperature", + "displayName": "temperature", + "displayNameEvent": "temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0 + } + ] + }, + { + "id": "6db42501-5451-4aac-9525-5f886b3188e2", + "name": "singleChannelSwitch", + "displayName": "1-channel switch", + "interfaces": [ ], + "createMethods": ["discovery"], + "paramTypes": [ + { + "id": "c9d6b7fd-fa21-473a-b5ed-9c5227749f06", + "name": "address", + "displayName": "Address", + "type": "QString", + "readOnly": true + }, + { + "id": "6efc8cb6-81ae-45c0-8910-708401d1ba68", + "name": "type", + "displayName": "Type", + "type": "QString", + "inputType": "TextLine", + "readOnly": true + } + ], + "stateTypes": [ + { + "id": "ca10a9fd-e4e0-4608-a2d2-6a4ce9644f40", + "name": "digitalOutput", + "displayName": "Digital output", + "displayNameEvent": "Digital output changed", + "displayNameAction": "Set digital output", + "type": "bool", + "defaultValue": false, + "writable": true + } + ] + }, + { + "id": "023f2b93-61e1-4422-97f5-3d5c14a6628f", + "name": "dualChannelSwitch", + "displayName": "2-channel switch", + "interfaces": [ ], + "createMethods": ["discovery"], + "paramTypes": [ + { + "id": "b9a1a23d-1fbf-4849-8aa2-2855e7deaf84", + "name": "address", + "displayName": "Address", + "type": "QString", + "readOnly": true + }, + { + "id": "b71ed57b-e768-4119-829e-a0f2c9fa5e18", + "name": "type", + "displayName": "Type", + "type": "QString", + "inputType": "TextLine", + "readOnly": true + } + ], + "stateTypes": [ + { + "id": "f8b6b4a7-355c-4580-a676-8a4d0d619ff9", + "name": "digitalOutput1", + "displayName": "Digital output 1", + "displayNameEvent": "Digital output 1 changed", + "displayNameAction": "Set digital output 1", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "82a78aed-5994-4af5-aecb-1806be5de1f3", + "name": "digitalOutput2", + "displayName": "Digital output 2", + "displayNameEvent": "Digital output 2 changed", + "displayNameAction": "Set digital output 2", + "type": "bool", + "defaultValue": false, + "writable": true + + } + ] + }, + { + "id": "71691119-3bda-4424-b853-1a00f21086e1", + "name": "eightChannelSwitch", + "displayName": "8-channel switch", + "interfaces": [ ], + "createMethods": ["discovery"], + "paramTypes": [ + { + "id": "e3e6e596-0cd4-42a3-8401-ccf6349314b7", + "name": "address", + "displayName": "Address", + "type": "QString", + "readOnly": true + }, + { + "id": "34c8f771-4141-4183-9eaf-becbaf362ac8", + "name": "type", + "displayName": "Type", + "type": "QString", + "inputType": "TextLine", + "readOnly": true + } + ], + "stateTypes": [ + { + "id": "78fa12c0-246c-4112-8be6-5943d3c3cda5", + "name": "digitalOutput1", + "displayName": "Digital output 1", + "displayNameEvent": "Digital output 1 changed", + "displayNameAction": "Set digital output 1", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "c7d2f4a8-2b13-4a48-81a8-72f4908c775b", + "name": "digitalOutput2", + "displayName": "Digital output 2", + "displayNameEvent": "Digital output 2 changed", + "displayNameAction": "Set digital output 2", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "4b2ac595-eba9-4364-8cd7-00ff8bccda5a", + "name": "digitalOutput3", + "displayName": "Digital output 3", + "displayNameEvent": "Digital output 3 changed", + "displayNameAction": "Set digital output 3", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "bbbd1863-ef04-4687-803d-3c9ccdfc8d8f", + "name": "digitalOutput4", + "displayName": "Digital output 4", + "displayNameEvent": "Digital output 4 changed", + "displayNameAction": "Set digital output 4", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "50855d2b-a700-4030-8674-fee00cc0b4e2", + "name": "digitalOutput5", + "displayName": "Digital output 5", + "displayNameEvent": "Digital output 5 changed", + "displayNameAction": "Set digital output 5", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "a91ce593-09ba-4754-8a2e-e3f507313585", + "name": "digitalOutput6", + "displayName": "Digital output 6", + "displayNameEvent": "Digital output 6 changed", + "displayNameAction": "Set digital output 6", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "5f46047c-b00d-486f-b169-b738fbc89cdb", + "name": "digitalOutput7", + "displayName": "Digital output 7", + "displayNameEvent": "Digital output 7 changed", + "displayNameAction": "Set digital output 7", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "63334a17-0847-4f53-8007-1b5e72b88aa8", + "name": "digitalOutput8", + "displayName": "Digital output 8", + "displayNameEvent": "Digital output 8 changed", + "displayNameAction": "Set digital output 8", + "type": "bool", + "defaultValue": false, + "writable": true + } + ] + } + ] + } + ] +} diff --git a/onewire/onewire.cpp b/onewire/onewire.cpp new file mode 100644 index 00000000..59009200 --- /dev/null +++ b/onewire/onewire.cpp @@ -0,0 +1,280 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Bernhard Trinnes * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "onewire.h" +#include "extern-plugininfo.h" + +OneWire::OneWire(QObject *parent) : + QObject(parent) +{ + +} + +OneWire::~OneWire() +{ + OW_finish(); +} + +bool OneWire::init(const QByteArray &owfsInitArguments) +{ + //QByteArray initArguments; + //Test OWFS arguments + //initArguments.append("--fake 28 --fake 10"); //fake temperature sensors + //initArguments.append("--fake 29 --fake 12 --fake 05"); //fake temperature sensor + + //Test i2c + //initArguments.append("--i2c=ALL:ALL"); + + if (OW_init(owfsInitArguments) < 0) { + qWarning(dcOneWire()) << "ERROR initialising one wire" << strerror(errno); + return false; + } + m_path = "/"; + return true; +} + +bool OneWire::discoverDevices() +{ + char *dirBuffer = nullptr; + size_t dirLength ; + + if (OW_get(m_path, &dirBuffer, &dirLength) < 0) { + qWarning(dcOneWire()) << "DIRECTORY ERROR" << strerror(errno); + return false; + } + qDebug(dcOneWire()) << "Directory has members" << dirBuffer; + + QList dirMembers ; + dirMembers = QByteArray(dirBuffer, dirLength).split(','); + free(dirBuffer); + + QList oneWireDevices; + foreach(QByteArray member, dirMembers) { + + /* Other system members: + * bus.0 + * uncached + * settings + * system + * statistics + * structure + * simultaneous + * alarm + */ + + int family = member.split('.').first().toInt(nullptr, 16); + if (family != 0) { + member.remove(member.indexOf('/'), 1); + QByteArray type; + OneWireDevice device; + device.family = family; + device.address = member; + device.id = member.split('.').last(); + device.type = getValue(member, "type"); + oneWireDevices.append(device); + } + } + if(!oneWireDevices.isEmpty()) { + emit devicesDiscovered(oneWireDevices); + } + return true; +} + +bool OneWire::interfaceIsAvailable() +{ + return true; +} + +bool OneWire::isConnected(const QByteArray &address) +{ + Q_UNUSED(address) + QByteArray fullPath; + fullPath.append(m_path); + fullPath.append(address); + fullPath.append('\0'); + if(OW_present(fullPath) < 0) + return false; + return true; +} + +/* Takes a path and filename and prints the 1-wire value */ +/* makes sure the bridging "/" in the path is correct */ +/* watches for total length and free allocated space */ +QByteArray OneWire::getValue(const QByteArray &address, const QByteArray &type) +{ + char * getBuffer ; + size_t getLength ; + + QByteArray devicePath; + devicePath.append(m_path); + if(!m_path.endsWith('/')) + devicePath.append('/'); + devicePath.append(address); + devicePath.append('/'); + devicePath.append(type); + devicePath.append('\0'); + + if (OW_get(devicePath, &getBuffer, &getLength) < 0) { + qWarning(dcOneWire()) << "ERROR reading" << devicePath << strerror(errno); + } + + qDebug(dcOneWire()) << "Device value" << devicePath << getBuffer; + + QByteArray value = QByteArray(getBuffer, getLength); + free(getBuffer); + return value; +} + +void OneWire::setValue(const QByteArray &address, const QByteArray &type, const QByteArray &value) +{ + Q_UNUSED(value) + QByteArray devicePath; + devicePath.append(m_path); + if(!m_path.endsWith('/')) + devicePath.append('/'); + devicePath.append(address); + devicePath.append('/'); + devicePath.append(type); + devicePath.append('\0'); + + if (OW_put(devicePath, value, value.length()) < 0) { + qWarning(dcOneWire()) << "ERROR reading" << devicePath << strerror(errno); + } +} + +double OneWire::getTemperature(const QByteArray &address) +{ + QByteArray temperature = getValue(address, "temperature"); + qDebug(dcOneWire()) << "Temperature" << temperature << temperature.replace(',','.').toDouble(); + return temperature.toDouble(); +} + +QByteArray OneWire::getType(const QByteArray &address) +{ + QByteArray type = getValue(address, "type"); + return type; +} + +bool OneWire::getSwitchOutput(const QByteArray &address, SwitchChannel channel) +{ + QByteArray c; + c.append("PIO."); + switch (channel) { + case PIO_A: + c.append('A'); + break; + case PIO_B: + c.append('B'); + break; + case PIO_C: + c.append('C'); + break; + case PIO_D: + c.append('D'); + break; + case PIO_E: + c.append('E'); + break; + case PIO_F: + c.append('F'); + break; + case PIO_G: + c.append('G'); + break; + case PIO_H: + c.append('H'); + break; + } + QByteArray state = getValue(address, c); + qDebug(dcOneWire()) << "Switch state" << state.toInt(); + return state.toInt(); +} + +bool OneWire::getSwitchInput(const QByteArray &address, SwitchChannel channel) +{ + QByteArray c; + c.append("sensed."); + switch (channel) { + case PIO_A: + c.append('A'); + break; + case PIO_B: + c.append('B'); + break; + case PIO_C: + c.append('C'); + break; + case PIO_D: + c.append('D'); + break; + case PIO_E: + c.append('E'); + break; + case PIO_F: + c.append('F'); + break; + case PIO_G: + c.append('G'); + break; + case PIO_H: + c.append('H'); + break; + } + QByteArray state = getValue(address, c); + qDebug(dcOneWire()) << "Switch state" << state.toInt(); + return state.toInt(); +} + +void OneWire::setSwitchOutput(const QByteArray &address, SwitchChannel channel, bool state) +{ + QByteArray c; + c.append("PIO."); + switch (channel) { + case PIO_A: + c.append('A'); + break; + case PIO_B: + c.append('B'); + break; + case PIO_C: + c.append('C'); + break; + case PIO_D: + c.append('D'); + break; + case PIO_E: + c.append('E'); + break; + case PIO_F: + c.append('F'); + break; + case PIO_G: + c.append('G'); + break; + case PIO_H: + c.append('H'); + break; + } + setValue(address, c, QVariant(state).toByteArray()); +} + + diff --git a/onewire/onewire.h b/onewire/onewire.h new file mode 100644 index 00000000..91fef048 --- /dev/null +++ b/onewire/onewire.h @@ -0,0 +1,85 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Bernhard Trinnes * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef ONEWIRE_H +#define ONEWIRE_H + +#include "owcapi.h" + +#include + +class OneWire : public QObject +{ + Q_OBJECT +public: + enum OneWireProperty { + Address, //The entire 64-bit unique ID + Crc, //The 8-bit error correction + Family, //The 8-bit family code + Id, //The 48-bit middle portion of the unique ID number. + Locator, //Uses an extension of the 1-wire design from iButtonLink company that associated 1-wire physical connections with a unique 1-wire code. + Type //Part name assigned by Dallas Semi. E.g. DS2401 + }; + + enum SwitchChannel { + PIO_A, + PIO_B, + PIO_C, + PIO_D, + PIO_E, + PIO_F, + PIO_G, + PIO_H + }; + + struct OneWireDevice { + QByteArray address; + int family; + QByteArray id; + QByteArray type; + }; + + explicit OneWire(QObject *parent = nullptr); + ~OneWire(); + bool init(const QByteArray &owfsInitArguments); + + QByteArray getPath(); + bool discoverDevices(); + bool interfaceIsAvailable(); + bool isConnected(const QByteArray &address); + + double getTemperature(const QByteArray &address); + QByteArray getType(const QByteArray &address); + bool getSwitchOutput(const QByteArray &address, SwitchChannel channel); + void setSwitchOutput(const QByteArray &address, SwitchChannel channel, bool state); + bool getSwitchInput(const QByteArray &address, SwitchChannel channel); + +private: + QByteArray m_path; + QByteArray getValue(const QByteArray &address, const QByteArray &deviceType); + void setValue(const QByteArray &address, const QByteArray &deviceType, const QByteArray &value); + +signals: + void devicesDiscovered(QList devices); +}; + +#endif // ONEWIRE_H diff --git a/onewire/onewire.pro b/onewire/onewire.pro new file mode 100644 index 00000000..d7654850 --- /dev/null +++ b/onewire/onewire.pro @@ -0,0 +1,17 @@ +include(../plugins.pri) + +TARGET = $$qtLibraryTarget(nymea_devicepluginonewire) + +LIBS += \ + -low \ + -lowcapi \ + +SOURCES += \ + devicepluginonewire.cpp \ + onewire.cpp \ + +HEADERS += \ + devicepluginonewire.h \ + onewire.h \ + +