Skip to content

Commit 60f1075

Browse files
authored
First Release
- Added HA discovery - Pylint conformity
1 parent 5453ffe commit 60f1075

File tree

7 files changed

+638
-5
lines changed

7 files changed

+638
-5
lines changed

fill_oh_things_template.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# python
2+
#
3+
# This file is part of the mqttDisplayClient distribution:
4+
# (https://github.com/olialb/mqttDisplayClient.
5+
# Copyright (c) 2025 Oliver Albold.
6+
#
7+
# This program is free software: you can redistribute it and/or modify
8+
# it under the terms of the GNU General Public License as published by
9+
# the Free Software Foundation, version 3.
10+
#
11+
# This program is distributed in the hope that it will be useful, but
12+
# WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
# General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
#
19+
"""Module providing a function to fill an openhab things template"""
20+
21+
import configparser
22+
import logging
23+
import os
24+
import sys
25+
26+
#
27+
# initialize logger
28+
#
29+
LOG = logging.getLogger("OHThinksTemplate")
30+
logging.basicConfig()
31+
32+
#
33+
# global constants
34+
#
35+
CONFIG_FILE = "mqttBH1750Client.ini" # name of the ini file
36+
37+
# import from the configuration file only the feature configuraion
38+
CFG = configparser.ConfigParser()
39+
40+
# try to open ini file
41+
try:
42+
if os.path.exists(CONFIG_FILE) is False:
43+
LOG.critical("Config file not found '%s'!", CONFIG_FILE)
44+
else:
45+
CFG.read(CONFIG_FILE)
46+
except OSError:
47+
LOG.error("Error while reading ini XXX file: %s", {CONFIG_FILE})
48+
sys.exit()
49+
50+
# read ini file values
51+
try:
52+
# read logging config
53+
logLevel = CFG["logging"]["level"]
54+
if logLevel in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]:
55+
LOG.setLevel(logLevel)
56+
else:
57+
raise KeyError(logLevel)
58+
59+
deviceName = CFG["global"]["deviceName"]
60+
topicRoot = CFG["global"]["topicRoot"]
61+
62+
except KeyError as error:
63+
LOG.error("Error while reading ini file: %s", error)
64+
sys.exit()
65+
66+
with open("BH1750.things.template", encoding="utf-8") as f:
67+
s = f.read()
68+
69+
s = s.format(name=deviceName, baseTopic=topicRoot)
70+
71+
with open("BH1750.things", "w", encoding="utf-8") as text_file:
72+
text_file.write(s)

ha_discover.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# python
2+
#
3+
# This file is part of the mqttDisplayClient distribution:
4+
# (https://github.com/olialb/mqttDisplayClient).
5+
# Copyright (c) 2025 Oliver Albold.
6+
#
7+
# This program is free software: you can redistribute it and/or modify
8+
# it under the terms of the GNU General Public License as published by
9+
# the Free Software Foundation, version 3.
10+
#
11+
# This program is distributed in the hope that it will be useful, but
12+
# WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
# General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
#
19+
"""Module implements a class for home assistant discovery content
20+
in mqtt topics
21+
"""
22+
23+
import uuid
24+
import json
25+
26+
#
27+
# this file defines everthing whats needed to publish
28+
# the topics for homeassitant device discovery
29+
#
30+
31+
32+
class HADiscovery:
33+
"""Implements methods to create content for home assitant
34+
auto discovery mqtt topics"""
35+
36+
def __init__(
37+
self,
38+
device_name="MyDevice",
39+
base="homeassitant",
40+
manufacturer="MyCompany",
41+
model="MyModel",
42+
):
43+
"""Create class default values"""
44+
self.uid = "_" + str(hex(uuid.getnode())).replace("0x", "") + "_"
45+
self.device_name = device_name
46+
self.base = base
47+
self.manufacturer = manufacturer
48+
self.model = model
49+
50+
def device(self):
51+
"""json content of a device"""
52+
js = {}
53+
js["name"] = self.device_name
54+
js["identifiers"] = self.device_name + "_" + self.uid
55+
js["manufacturer"] = self.manufacturer
56+
js["model"] = self.model
57+
return js
58+
59+
def sensor( # pylint: disable=too-many-arguments, too-many-positional-arguments
60+
self,
61+
name,
62+
state_topic,
63+
value_template=None,
64+
device_class=None,
65+
unit=None,
66+
icon=None
67+
):
68+
"""json content of a sensor"""
69+
uid = self.uid
70+
topic = self.base + "/sensor/" + uid + "/" + name.replace(" ", "_") + "/config"
71+
js = {}
72+
js["name"] = name
73+
js["unique_id"] = self.uid + "_" + name.replace(" ", "_")
74+
js["state_topic"] = state_topic
75+
if unit is not None:
76+
js["unit_of_measurement"] = unit
77+
if value_template is not None:
78+
js["value_template"] = "{{ value_json." + value_template + " }}"
79+
if device_class is not None:
80+
js["device_class"] = device_class
81+
if icon is not None:
82+
js['icon'] = "mdi:"+icon
83+
js["device"] = self.device()
84+
return topic, json.dumps(js)
85+
86+
def switch(self, name, state_topic, value_template=None):
87+
"""json content of a switch"""
88+
uid = self.uid
89+
topic = self.base + "/switch/" + uid + "/" + name.replace(" ", "_") + "/config"
90+
js = {}
91+
js["name"] = name
92+
js["unique_id"] = self.uid + "_" + name.replace(" ", "_")
93+
js["command_topic"] = state_topic + "/set"
94+
js["state_topic"] = state_topic
95+
js["payload_on"] = "ON"
96+
js["payload_off"] = "OFF"
97+
js["state_on"] = "ON"
98+
js["state_off"] = "OFF"
99+
if value_template is not None:
100+
js["value_template"] = "{{ value_json." + value_template + " }}"
101+
js["device"] = self.device()
102+
return topic, json.dumps(js)
103+
104+
def text(self, name, state_topic, value_template=None):
105+
"""json content of a text entity"""
106+
uid = self.uid
107+
topic = self.base + "/text/" + uid + "/" + name.replace(" ", "_") + "/config"
108+
js = {}
109+
js["name"] = name
110+
js["unique_id"] = self.uid + "_" + name.replace(" ", "_")
111+
js["command_topic"] = state_topic + "/set"
112+
js["state_topic"] = state_topic
113+
if value_template is not None:
114+
js["value_template"] = "{{ value_json." + value_template + " }}"
115+
js["device"] = self.device()
116+
return topic, json.dumps(js)
117+
118+
def select(self, name, state_topic, options, value_template=None):
119+
"""json content of a select entity"""
120+
uid = self.uid
121+
topic = self.base + "/select/" + uid + "/" + name.replace(" ", "_") + "/config"
122+
js = {}
123+
js["name"] = name
124+
js["unique_id"] = self.uid + "_" + name.replace(" ", "_")
125+
js["command_topic"] = state_topic + "/set"
126+
js["state_topic"] = state_topic
127+
js["options"] = options
128+
if value_template is not None:
129+
js["value_template"] = "{{ value_json." + value_template + " }}"
130+
js["device"] = self.device()
131+
return topic, json.dumps(js)
132+
133+
def light( # pylint: disable=too-many-arguments, too-many-positional-arguments
134+
self,
135+
name,
136+
state_topic,
137+
brightness_topic,
138+
value_template_state=None,
139+
value_tmpl_brightness=None,
140+
brightness_scale=100,
141+
):
142+
"""json content of a light"""
143+
uid = self.uid
144+
topic = self.base + "/light/" + uid + "/" + name.replace(" ", "_") + "/config"
145+
js = {}
146+
js["name"] = name
147+
js["unique_id"] = self.uid + "_" + name.replace(" ", "_")
148+
js["command_topic"] = state_topic + "/set"
149+
js["state_topic"] = state_topic
150+
js["payload_on"] = "ON"
151+
js["payload_off"] = "OFF"
152+
js["state_on"] = "ON"
153+
js["state_off"] = "OFF"
154+
js["brightness_scale"] = brightness_scale
155+
js["brightness_command_topic"] = brightness_topic + "/set"
156+
js["brightness_state_topic"] = brightness_topic
157+
if value_template_state is not None:
158+
js["state_value_template"] = "{{ value_json." + value_template_state + " }}"
159+
if value_tmpl_brightness is not None:
160+
js["brightness_value_template"] = (
161+
"{{ value_json." + value_tmpl_brightness + " }}"
162+
)
163+
js["device"] = self.device()
164+
return topic, json.dumps(js)

mqttBH1750Client.ini

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,25 @@ fullPublishCycle=20
3535
[logging]
3636
#configure the log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
3737
level=WARNING
38+
#log file path
39+
path=log
40+
#log file name
41+
file=mqttBH1750Client.log
42+
43+
[feature]
44+
#enable home assitant auto discovery
45+
haDiscover=enabled
3846

3947
[bh1750]
4048
#i2c address of the bh1750
4149
i2cAddr=0x23
4250
#mode in which bh1750 is used
4351
mode=0x10
4452

53+
[haDiscover]
54+
#device name used in ha discover. You need to adapt it if you have more than one devives in your network
55+
deviceName=kiosk01
56+
#standard base topic of home assitant discovers. Only need to be changed
57+
base=homeassistant
58+
4559

mqttBH1750Client.service.template

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ Restart=on-failure
2424
User=$USER
2525
Environment=PYTHONPATH=/home/$USER/mqttBH1750Client
2626
WorkingDirectory=/home/$USER/mqttBH1750Client
27-
ExecStart=/home/$USER/mqttBH1750Client/venv/bin/python mqttBH1750Client.py
28-
#ExecStart=bash -c 'cd/home/$USER/mqttBH1750Client && ./mqttBH1750Client'
27+
ExecStart=/home/$USER/mqttBH1750Client/venv/bin/python mqtt_bh1750_client.py
2928

3029
[Install]
3130
WantedBy=multi-user.target

0 commit comments

Comments
 (0)