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

Knx interfacer #200

Open
wants to merge 4 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
37 changes: 37 additions & 0 deletions conf/interfacer_examples/KNX/knx.emonhub.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[[KNX]]
Type = EmonHubKNXInterfacer
[[[init_settings]]]
gateway_ip = 192.168.254.1
port = 3671
[[[runtimesettings]]]
pubchannels = ToEmonCMS,
read_interval = 5
validate_checksum = False
nodeid=1
nodename = KNX
[[[[meters]]]]
[[[[[compteur]]]]]
[[[[[[voltage]]]]]]
group=10/0/1
eis=DPT-14
[[[[[[intensite]]]]]]
group=10/1/1
eis=DPT-14
[[[[[[puissance]]]]]]
group=10/2/1
eis=DPT-14
[[[[[[consommation]]]]]]
group=10/3/1
eis=DPT-12
[[[[[[consommationWh]]]]]]
group=10/5/1
eis=DPT-12
[[[[[compteurNew]]]]]
[[[[[[voltage]]]]]]
group=10/0/2
eis=DPT-14
[[[[[[intensite]]]]]]
group=10/1/2
eis=DPT-14


66 changes: 66 additions & 0 deletions conf/interfacer_examples/KNX/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
### KNX Reader to read value from knx group using a knx Gateway

KNX is a international standard for home automation.
KNX is based on a bus, where each device can communicate using knx group.
A Knx group is address using a knx address group notation of the form x/y/z.

To configure this interfacers, you would need:

to fill the global init_settings, mainly:

- **gateway_ip** The ip address of your ip gateway device.
- **port** The port of the gateway device (3671 is the default).
- **local_ip** If your server have multiple ip interface, indicate the ip of the interface link to the gateway device.

In runtimeseetings, you will have to list the group you want to read.
You can make some grouping by indicating a devicename under the meters section

Each device can contains single or many group read section of the form:
[[[[[[groupName]]]]]]
group=10/0/1
eis=DPT-14

- **groupName:** Will indicate the name of the group, indicate what you want, it will be the name of the input in emoncms inputs.
- **group:** The address of the group
- **eis:** The KNX type use for this group (please refer to KNX official documentation for the list).


```text
[[KNX]]
Type = EmonHubKNXInterfacer
[[[init_settings]]]
gateway_ip = 192.168.254.1
port = 3671
[[[runtimesettings]]]
pubchannels = ToEmonCMS,
read_interval = 5
validate_checksum = False
nodeid=1
nodename = KNX
[[[[meters]]]]
[[[[[compteur]]]]]
[[[[[[voltage]]]]]]
group=10/0/1
eis=DPT-14
[[[[[[intensite]]]]]]
group=10/1/1
eis=DPT-14
[[[[[[puissance]]]]]]
group=10/2/1
eis=DPT-14
[[[[[[consommation]]]]]]
group=10/3/1
eis=DPT-12
[[[[[[consommationWh]]]]]]
group=10/5/1
eis=DPT-12
[[[[[compteurNew]]]]]
[[[[[[voltage]]]]]]
group=10/0/2
eis=DPT-14
[[[[[[intensite]]]]]]
group=10/1/2
eis=DPT-14



262 changes: 262 additions & 0 deletions src/interfacers/EmonHubKNXInterfacer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
import time
import json
import re
import Cargo
import serial
import struct
import asyncio
import concurrent.futures
import threading

from emonhub_interfacer import EmonHubInterfacer
from xknx import XKNX
from xknx.io import ConnectionConfig, ConnectionType

from xknx.devices import Light
from xknx.devices import NumericValue
from xknx.devices import RawValue
from xknx.devices import Sensor
from xknx.dpt import DPTArray
from xknx.telegram import GroupAddress, Telegram
from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite
from xknx.core import ValueReader

"""
[[KNX]]
Type = EmonHubKNXInterfacer
[[[init_settings]]]
gateway_ip = 192.168.254.40
port = 3691
local_ip = 192.168.254.1
[[[runtimesettings]]]
pubchannels = ToEmonCMS,
read_interval = 10
validate_checksum = False
nodename = KNX
[[[[meters]]]]
[[[[[compteur]]]]]
group=1/1/1
eis=DPT-14
[[[[[[consommationWh]]]]]]
group=10/5/1
eis=DPT-12
"""

"""class EmonHubKNXInterfacer

KNX interfacer

"""

class EmonHubKNXInterfacer(EmonHubInterfacer):

def __init__(self, name, gateway_ip="127.0.0.1", local_ip="127.0.0.1", port=3671):
"""Initialize Interfacer

"""
# Initialization
super(EmonHubKNXInterfacer, self).__init__(name)

# This line will stop the default values printing to logfile at start-up
# self._settings.update(self._defaults)

# Interfacer specific settings
self._KNX_settings = {'read_interval': 10.0,
'nodename':'KNX',
'validate_checksum': True,
'meters':[]}

self._last_read_time = 0

try:
self.loop = asyncio.get_event_loop()

task = self.loop.create_task(self.initKnx(gateway_ip, local_ip))
self.loop.run_until_complete(task)

self.cargoList = {}

except ModuleNotFoundError as err:
self._log.error(err)
self.ser = False


def action(self):
super().action()


async def initKnx(self, gateway_ip, local_ip):
connection_config = ConnectionConfig(
connection_type=ConnectionType.TUNNELING,
gateway_ip=gateway_ip,
local_ip = local_ip
)

self._log.info("Connect to KNX Gateway : " + gateway_ip)
self.xknx = XKNX(connection_config=connection_config, device_updated_cb=self.device_updated_cb, daemon_mode=False)

async def startKnx(self):
await self.xknx.start()


#@asyncio.coroutine
async def device_updated_cb(self, device):
value = device.resolve_state()
name = device.name
unit = device.unit_of_measurement()

self._log.debug("Device:" + name + ' <> ' + str(value))

namePart = name.split("_")
meter = namePart[0]
key = namePart[1]

meterObj=self._settings['meters'][meter]
dptConf = meterObj[key]
if 'divider' in dptConf:
divider = dptConf["divider"]
if divider != '':
value = float(value) / float(divider)



result = {}
result[key] = [value,unit]


if meter in self.cargoList:
c = self.cargoList[meter]
self.add_result_to_cargo(c, result)
else:
cargoNew = Cargo.new_cargo("", False,[], [])
cargoNew.nodeid = meter
self.cargoList[meter] = cargoNew
self.add_result_to_cargo(cargoNew, result)



def add_result_to_cargo(self, cargo, result):
if result != None:

for key in result:
cargo.names.append(key)
cargo.realdata.append(result[key][0])
else:
self._log.debug("Decoded KNX data: None")


async def setupSensor(self):
metters = self._settings['meters']

self.sensor={}
for metter in metters:
dpPoint = metters[metter]

for dpKey in dpPoint:
dpConfig = dpPoint[dpKey]

group = dpConfig["group"]
eis = dpConfig["eis"]

self.sensor[metter+"_"+dpKey] = Sensor(self.xknx, metter + "_" + dpKey, value_type=eis, group_address_state=group, always_callback=True)




def add(self, cargo):
self.buffer.storeItem(f)


async def waitSensor(self):
pass


def read(self):
"""Read data and process

Return data as a list: [NodeID, val1, val2]

"""
interval = int(self._settings['read_interval'])
if time.time() - self._last_read_time < interval:
return

self._last_read_time = time.time()

#self.displayCargo("read")
result = self.cargoList;
self.cargoList ={};

return result;

def start(self):
self._log.info("Start KNX interface")

task = self.loop.create_task(self.setupSensor())
self.loop.run_until_complete(task)

task = self.loop.create_task(self.startKnx())
self.loop.run_until_complete(task)


self.updater = self.Updater(self)
self.updater.start()

super().start()


def set(self, **kwargs):
for key, setting in self._KNX_settings.items():
# Decide which setting value to use
if key in kwargs:
setting = kwargs[key]
else:
setting = self._KNX_settings[key]

if key in self._settings and self._settings[key] == setting:
continue
elif key == 'read_interval':
self._log.info("Setting %s read_interval: %s", self.name, setting)
self._settings[key] = float(setting)
continue
elif key == 'nodename':
self._log.info("Setting %s nodename: %s", self.name, setting)
self._settings[key] = str(setting)
continue
elif key == 'validate_checksum':
self._log.info("Setting %s validate_checksum: %s", self.name, setting)
self._settings[key] = True
if setting=='False':
self._settings[key] = False
continue
elif key == 'meters':
self._log.info("Setting %s meters: %s", self.name, json.dumps(setting))
self._settings['meters'] = {}
for meter in setting:
# default
address = 1
meter_type = "standard"
records = []

self._settings['meters'][meter] = setting[meter]
continue
else:
self._log.warning("'%s' is not valid for %s: %s", setting, self.name, key)

# include kwargs from parent
super().set(**kwargs)



class Updater(threading.Thread):
def __init__(self, knxIntf):
super().__init__()
self.loop = asyncio.get_event_loop()
self.knxIntf = knxIntf

pass

def run(self):
while not self.knxIntf.stop:
self.loop.run_until_complete(asyncio.sleep(1))
pass
1 change: 1 addition & 0 deletions src/interfacers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"EmonHubRedisInterfacer",
"EmonHubSDM120Interfacer",
"EmonHubMBUSInterfacer",
"EmonHubKNXInterfacer",
"EmonHubMinimalModbusInterfacer",
"EmonHubBleInterfacer",
"EmonHubGoodWeInterfacer",
Expand Down