-
Notifications
You must be signed in to change notification settings - Fork 83
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
base: master
Are you sure you want to change the base?
Knx interfacer #200
Changes from all commits
61a0e1f
30fe1c0
da7b6cb
19bff76
501595c
e7587f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
|
||
|
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 | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a throught, is it threadsafe to use run_until_complete like that ? |
||
|
||
self.cargoList = {} | ||
|
||
except ModuleNotFoundError as err: | ||
self._log.error(err) | ||
self.ser = False | ||
|
||
|
||
def action(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure this is really needed. |
||
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.debug("Connect to KNX Gateway : " + gateway_ip) | ||
self.xknx = XKNX(connection_config=connection_config, connection_state_changed_cb = self.connection_state_changed_cb,device_updated_cb=self.device_updated_cb, daemon_mode=False) | ||
|
||
async def startKnx(self): | ||
try: | ||
await self.xknx.start() | ||
except Exception as err: | ||
self._log.error("KNX Error start:") | ||
self._log.error(err); | ||
|
||
def connection_state_changed_cb(self, state): | ||
self._log.debug("KNX CnxUpdate:" ) | ||
self._log.debug(state) | ||
|
||
|
||
def device_updated_cb(self, device): | ||
value = device.resolve_state() | ||
name = device.name | ||
unit = device.unit_of_measurement() | ||
|
||
self._log.info("Device:" + name + ' <> ' + str(value)) | ||
|
||
pos = name.index("_") | ||
meter = name[0:pos] | ||
key = name[pos+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.info("Decoded KNX data: None") | ||
|
||
|
||
async def setupSensor(self): | ||
metters = self._settings['meters'] | ||
|
||
self.sensor={} | ||
for metter in metters: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. possible to use items() like you do below ? |
||
dpPoint = metters[metter] | ||
|
||
for dpKey in dpPoint: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same I think you can iterate with items() use var name like |
||
dpConfig = dpPoint[dpKey] | ||
|
||
group = dpConfig["group"] | ||
eis = dpConfig["eis"] | ||
|
||
self._log.debug("add Sensors:" + metter+"_"+dpKey + ' <> ' + group) | ||
self.sensor[metter+"_"+dpKey] = Sensor(self.xknx, metter + "_" + dpKey, value_type=eis, group_address_state=group, always_callback=True) | ||
self.xknx.devices.async_add(self.sensor[metter+"_"+dpKey]) | ||
|
||
|
||
|
||
|
||
def add(self, cargo): | ||
self.buffer.storeItem(f) | ||
|
||
|
||
async def waitSensor(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this used somewhere ? |
||
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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could do |
||
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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you dont give any var when you initialize the updater in self.start. What is 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
xknx should be integrated in the dependencies....or maybe just a mention in the interfacer README