Merge branch 'mqtt-refactoring' of ch1p.io:homekit into mqtt-refactoring

This commit is contained in:
Evgeny Zinoviev 2023-06-08 13:32:49 +03:00
commit 27234de929
51 changed files with 335 additions and 271 deletions

View File

@ -14,7 +14,7 @@ apscheduler~=3.9.1
psutil~=5.9.1
aioshutil~=1.1
scikit-image~=0.19.3
cerberus~=1.3.4
# following can be installed from debian repositories
# matplotlib~=3.5.0

View File

@ -65,7 +65,7 @@ class ESP32CameraNodeServer(MediaNodeServer):
if __name__ == '__main__':
config.load('camera_node')
config.load_app('camera_node')
recorder_kwargs = {}
camera_type = CameraType(config['camera']['type'])

View File

@ -76,7 +76,7 @@ class ESP32CamCaptureDiffNode:
if __name__ == '__main__':
config.load('esp32cam_capture_diff_node')
config.load_app('esp32cam_capture_diff_node')
loop = asyncio.get_event_loop()
ESP32CamCaptureDiffNode()

View File

@ -13,7 +13,7 @@ if __name__ == '__main__':
if not os.getegid() == 0:
sys.exit('Must be run as root.')
config.load()
config.load_app()
try:
s = RelayServer(pinname=config.get('relayd.pin'),

View File

@ -1 +1,11 @@
from .config import ConfigStore, config, is_development_mode, setup_logging
from .config import (
Config,
config,
is_development_mode,
setup_logging,
app_config
)
from .validators import validate
from ._configs import (
LinuxBoardsConfig
)

View File

@ -0,0 +1,5 @@
from .config import ConfigUnit
class LinuxBoardsConfig(ConfigUnit):
NAME = 'linux_boards'

View File

@ -3,45 +3,116 @@ import yaml
import logging
import os
from . import validators
from os.path import join, isdir, isfile
from typing import Optional, Any, MutableMapping
from argparse import ArgumentParser
from ..util import parse_addr
def _get_config_path(name: str) -> str:
formats = ['toml', 'yaml']
dirname = join(os.environ['HOME'], '.config', name)
if isdir(dirname):
for fmt in formats:
filename = join(dirname, f'config.{fmt}')
if isfile(filename):
return filename
raise IOError(f'config not found in {dirname}')
else:
filenames = [join(os.environ['HOME'], '.config', f'{name}.{format}') for format in formats]
for file in filenames:
if isfile(file):
return file
raise IOError(f'config not found')
_my_validators = {}
class ConfigStore:
def _get_validator(name: str) -> Optional[callable]:
if hasattr(validators, f'{name}_validator'):
return getattr(validators, f'{name}_validator')
if name in _my_validators:
return _my_validators[name]
return None
def add_validator(name: str, f: callable):
_my_validators[name] = f
class ConfigUnit:
NAME = 'dumb'
data: MutableMapping[str, Any]
@classmethod
def get_config_path(cls, name=None) -> str:
if name is None:
name = cls.NAME
if name is None:
raise ValueError('get_config_path: name is none')
dirnames = (
join(os.environ['HOME'], '.config', 'homekit'),
'/etc/homekit'
)
for dirname in dirnames:
if isdir(dirname):
for fmt in ('toml', 'yaml'):
filename = join(dirname, f'{name}.{fmt}')
if isfile(filename):
return filename
raise IOError(f'config \'{name}\' not found')
def __init__(self, name=None):
self.data = {}
if self.NAME != 'dumb':
self.load_from(self.get_config_path())
self.validate()
elif name is not None:
self.NAME = name
def load_from(self, path: str):
if path.endswith('.toml'):
self.data = toml.load(path)
elif path.endswith('.yaml'):
with open(path, 'r') as fd:
self.data = yaml.safe_load(fd)
def validate(self):
v = _get_validator(self.NAME)
v(self.data)
def __getitem__(self, key):
return self.data[key]
def __setitem__(self, key, value):
raise NotImplementedError('overwriting config values is prohibited')
def __contains__(self, key):
return key in self.data
def get(self, key: str, default=None):
cur = self.data
pts = key.split('.')
for i in range(len(pts)):
k = pts[i]
if i < len(pts)-1:
if k not in cur:
raise KeyError(f'key {k} not found')
else:
return cur[k] if k in cur else default
cur = self.data[k]
raise KeyError(f'option {key} not found')
def get_addr(self, key: str):
return parse_addr(self.get(key))
def items(self):
return self.data.items()
class Config:
app_name: Optional[str]
app_config: ConfigUnit
def __init__(self):
self.data = {}
self.app_name = None
self.app_config = ConfigUnit()
def load(self, name: Optional[str] = None,
use_cli=True,
parser: ArgumentParser = None):
def load_app(self,
name: Optional[str] = None,
use_cli=True,
parser: ArgumentParser = None):
self.app_name = name
if (name is None) and (not use_cli):
@ -75,65 +146,32 @@ class ConfigStore:
log_default_fmt = args.log_default_fmt
if not no_config and path is None:
path = _get_config_path(name)
path = ConfigUnit.get_config_path(name=name)
if no_config:
self.data = {}
else:
if path.endswith('.toml'):
self.data = toml.load(path)
elif path.endswith('.yaml'):
with open(path, 'r') as fd:
self.data = yaml.safe_load(fd)
if not no_config:
self.app_config.load_from(path)
if 'logging' in self:
if not log_file and 'file' in self['logging']:
log_file = self['logging']['file']
if log_default_fmt and 'default_fmt' in self['logging']:
log_default_fmt = self['logging']['default_fmt']
if 'logging' in self.app_config:
if not log_file and 'file' in self.app_config['logging']:
log_file = self.app_config['logging']['file']
if log_default_fmt and 'default_fmt' in self.app_config['logging']:
log_default_fmt = self.app_config['logging']['default_fmt']
setup_logging(log_verbose, log_file, log_default_fmt)
if use_cli:
return args
def __getitem__(self, key):
return self.data[key]
def __setitem__(self, key, value):
raise NotImplementedError('overwriting config values is prohibited')
def __contains__(self, key):
return key in self.data
def get(self, key: str, default=None):
cur = self.data
pts = key.split('.')
for i in range(len(pts)):
k = pts[i]
if i < len(pts)-1:
if k not in cur:
raise KeyError(f'key {k} not found')
else:
return cur[k] if k in cur else default
cur = self.data[k]
raise KeyError(f'option {key} not found')
def get_addr(self, key: str):
return parse_addr(self.get(key))
def items(self):
return self.data.items()
config = ConfigStore()
config = Config()
app_config = config.app_config
def is_development_mode() -> bool:
if 'HK_MODE' in os.environ and os.environ['HK_MODE'] == 'dev':
return True
return ('logging' in config) and ('verbose' in config['logging']) and (config['logging']['verbose'] is True)
return ('logging' in config.app_config) and ('verbose' in config.app_config['logging']) and (config.app_config['logging']['verbose'] is True)
def setup_logging(verbose=False, log_file=None, default_fmt=False):

View File

@ -0,0 +1,2 @@
from ._validators import *
from ._util import validate

View File

@ -0,0 +1,11 @@
import inspect
from cerberus import Validator, DocumentError
def validate(schema, data):
v = Validator(schema)
if not v.validate(data):
frame = inspect.currentframe().f_back
caller_name = frame.f_code.co_name
raise DocumentError(f'{caller_name}: failed to validate data: ' + v.errors)

View File

@ -0,0 +1,32 @@
from ._util import validate
__all__ = [
'linux_boards_validator'
]
def linux_boards_validator(data) -> None:
validate({
'type': 'dict',
'valuesrules': {
'type': 'dict',
'schema': {
'mdns': {'type': 'string', 'required': True},
'board': {'type': 'string', 'required': True},
'network': {'type': 'list', 'required': True, 'empty': False},
'ram': {'type': 'integer', 'required': True},
'ext_hdd': {
'type': 'list',
'schema': {
'type': 'dict',
'schema': {
'mountpoint': {'type': 'string', 'required': True},
'size': {'type': 'integer', 'required': True}
}
},
},
'services': {'type': 'list', 'empty': False},
'online': {'type': 'boolean', 'required': True}
}
}
}, data)

View File

@ -14,13 +14,17 @@ class MqttNode:
_modules: List[MqttModule]
_module_subscriptions: dict[str, MqttModule]
_node_id: str
_node_secret: str
_payload_callbacks: list[callable]
_wrapper: Optional[MqttWrapper]
def __init__(self, node_id: str):
def __init__(self,
node_id: str,
node_secret: Optional[str] = None):
self._modules = []
self._module_subscriptions = {}
self._node_id = node_id
self._node_secret = node_secret
self._payload_callbacks = []
self._logger = logging.getLogger(self.__class__.__name__)
self._wrapper = None
@ -42,7 +46,7 @@ class MqttNode:
payload = self._module_subscriptions[topic].handle_payload(self, topic, payload)
if isinstance(payload, MqttPayload):
for f in self._payload_callbacks:
f(payload)
f(self, payload)
def load_module(self, module_name: str, *args, **kwargs) -> MqttModule:
module = importlib.import_module(f'..module.{module_name}', __name__)
@ -78,3 +82,11 @@ class MqttNode:
@property
def id(self) -> str:
return self._node_id
@property
def secret(self) -> str:
return self._node_secret
@secret.setter
def secret(self, secret: str) -> None:
self._node_secret = secret

View File

@ -9,11 +9,15 @@ from ..util import strgen
class MqttWrapper(Mqtt):
_nodes: list[MqttNode]
def __init__(self, topic_prefix='hk', randomize_client_id=False):
def __init__(self,
topic_prefix='hk',
randomize_client_id=False,
clean_session=True):
client_id = config['mqtt']['client_id']
if randomize_client_id:
client_id += '_'+strgen(6)
super().__init__(clean_session=True, client_id=client_id)
super().__init__(clean_session=clean_session,
client_id=client_id)
self._nodes = []
self._topic_prefix = topic_prefix

View File

@ -41,7 +41,7 @@ class OtaPayload(MqttPayload):
class MqttOtaModule(MqttModule):
_ota_request: Optional[tuple[str, str, int]]
_ota_request: Optional[tuple[str, int]]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -52,9 +52,9 @@ class MqttOtaModule(MqttModule):
mqtt.subscribe_module("otares", self)
if self._ota_request is not None:
secret, filename, qos = self._ota_request
filename, qos = self._ota_request
self._ota_request = None
self.do_push_ota(secret, filename, qos)
self.do_push_ota(self._mqtt_node_ref.secret, filename, qos)
def handle_payload(self, mqtt: MqttNode, topic: str, payload: bytes) -> Optional[MqttPayload]:
if topic == 'otares':
@ -69,10 +69,9 @@ class MqttOtaModule(MqttModule):
qos=qos)
def push_ota(self,
secret: str,
filename: str,
qos: int):
if not self._initialized:
self._ota_request = (secret, filename, qos)
self._ota_request = (filename, qos)
else:
self.do_push_ota(secret, filename, qos)
self.do_push_ota(filename, qos)

View File

@ -64,9 +64,9 @@ class MqttRelayModule(MqttModule):
mqtt.subscribe_module('relay/status', self)
def switchpower(self,
enable: bool,
secret: str):
payload = MqttPowerSwitchPayload(secret=secret, state=enable)
enable: bool):
payload = MqttPowerSwitchPayload(secret=self._mqtt_node_ref.secret,
state=enable)
self._mqtt_node_ref.publish('relay/switch', payload=payload.pack())
def handle_payload(self, mqtt: MqttNode, topic: str, payload: bytes) -> Optional[MqttPayload]:

View File

@ -1,8 +1,8 @@
from enum import auto
# from enum import auto
from .._node import MqttNode
from .._module import MqttModule
from .._payload import MqttPayload
from ...util import HashableEnum
# from ...util import HashableEnum
from typing import Optional
from ...temphum import BaseSensor
@ -24,30 +24,31 @@ class MqttTemphumDataPayload(MqttPayload):
error: int
class MqttTempHumNodes(HashableEnum):
KBN_SH_HALL = auto()
KBN_SH_BATHROOM = auto()
KBN_SH_LIVINGROOM = auto()
KBN_SH_BEDROOM = auto()
KBN_BH_2FL = auto()
KBN_BH_2FL_STREET = auto()
KBN_BH_1FL_LIVINGROOM = auto()
KBN_BH_1FL_BEDROOM = auto()
KBN_BH_1FL_BATHROOM = auto()
KBN_NH_1FL_INV = auto()
KBN_NH_1FL_CENTER = auto()
KBN_NH_1LF_KT = auto()
KBN_NH_1FL_DS = auto()
KBN_NH_1FS_EZ = auto()
SPB_FLAT120_CABINET = auto()
# class MqttTempHumNodes(HashableEnum):
# KBN_SH_HALL = auto()
# KBN_SH_BATHROOM = auto()
# KBN_SH_LIVINGROOM = auto()
# KBN_SH_BEDROOM = auto()
#
# KBN_BH_2FL = auto()
# KBN_BH_2FL_STREET = auto()
# KBN_BH_1FL_LIVINGROOM = auto()
# KBN_BH_1FL_BEDROOM = auto()
# KBN_BH_1FL_BATHROOM = auto()
#
# KBN_NH_1FL_INV = auto()
# KBN_NH_1FL_CENTER = auto()
# KBN_NH_1LF_KT = auto()
# KBN_NH_1FL_DS = auto()
# KBN_NH_1FS_EZ = auto()
#
# SPB_FLAT120_CABINET = auto()
class MqttTempHumModule(MqttModule):
def __init__(self,
sensor: Optional[BaseSensor] = None,
write_to_database=False,
*args, **kwargs):
if sensor is not None:
kwargs['tick_interval'] = 10

View File

@ -44,7 +44,7 @@ flags_map = {
}
logger = logging.getLogger(__name__)
config.load('inverter_bot')
config.load_app('inverter_bot')
bot.initialize()
bot.lang.ru(

View File

@ -8,10 +8,10 @@ if __name__ == '__main__':
parser = ArgumentParser()
parser.add_argument('mode', type=str, choices=('sender', 'receiver'), nargs=1)
config.load('inverter_mqtt_util', parser=parser)
config.load_app('inverter_mqtt_util', parser=parser)
arg = parser.parse_args()
mqtt = MqttWrapper()
mqtt = MqttWrapper(clean_session=arg.mode[0] != 'receiver')
node = MqttNode(node_id='inverter')
module_kwargs = {}
if arg.mode[0] == 'sender':

View File

@ -556,7 +556,7 @@ logger = logging.getLogger(__name__)
# --------------------
if __name__ == '__main__':
config.load('ipcam_server')
config.load_app('ipcam_server')
open_database()

View File

@ -23,14 +23,14 @@ if __name__ == '__main__':
parser.add_argument('--node-secret', type=str,
help='node admin password')
config.load('mqtt_util', parser=parser)
config.load_app('mqtt_util', parser=parser)
arg = parser.parse_args()
if (arg.switch_relay is not None or arg.node_secret is not None) and 'relay' not in arg.modules:
raise ArgumentError(None, '--relay is only allowed when \'relay\' module included in --modules')
mqtt = MqttWrapper(randomize_client_id=True)
mqtt_node = MqttNode(node_id=arg.node_id)
mqtt_node = MqttNode(node_id=arg.node_id, node_secret=arg.node_secret)
mqtt.add_node(mqtt_node)
@ -44,9 +44,7 @@ if __name__ == '__main__':
if m == 'relay' and arg.switch_relay is not None:
if not arg.node_secret:
raise ArgumentError(None, '--switch-relay requires --node-secret')
module_instance.switchpower(mqtt_node,
arg.switch_relay == 1,
arg.node_secret)
module_instance.switchpower(arg.switch_relay == 1)
mqtt.configure_tls()
try:
@ -58,7 +56,7 @@ if __name__ == '__main__':
if not arg.node_secret:
raise ArgumentError(None, 'pushing OTA requires --node-secret')
ota_module.push_ota(arg.node_secret, arg.push_ota, 1)
ota_module.push_ota(arg.push_ota, 1)
while True:
sleep(0.1)

View File

@ -54,7 +54,7 @@ def main(mac: str,
if __name__ == '__main__':
config.load('openwrt_log_analyzer')
config.load_app('openwrt_log_analyzer')
for ap in config['openwrt_log_analyzer']['aps']:
state_file = config['simple_state']['file']
state_file = state_file.replace('.txt', f'-{ap}.txt')

View File

@ -46,7 +46,7 @@ if __name__ == '__main__':
parser.add_argument('--access-point', type=int, required=True,
help='access point number')
arg = config.load('openwrt_logger', parser=parser)
arg = config.load_app('openwrt_logger', parser=parser)
state = SimpleState(file=config['simple_state']['file'].replace('{ap}', str(arg.access_point)),
default={'seek': 0, 'size': 0})

View File

@ -41,7 +41,7 @@ from telegram.ext import (
)
logger = logging.getLogger(__name__)
config.load('polaris_kettle_bot')
config.load_app('polaris_kettle_bot')
primary_choices = (70, 80, 90, 100)
all_choices = range(

View File

@ -75,7 +75,7 @@ def main():
parser.add_argument('-t', '--temperature', dest='temp', type=int, default=tempmax,
choices=range(tempmin, tempmax+tempstep, tempstep))
arg = config.load('polaris_kettle_util', use_cli=True, parser=parser)
arg = config.load_app('polaris_kettle_util', use_cli=True, parser=parser)
if arg.mode == 'mqtt':
server = MqttServer()

View File

@ -16,7 +16,7 @@ from home.mqtt.module.temphum import MqttTemphumDataPayload
from home.mqtt.module.diagnostics import InitialDiagnosticsPayload, DiagnosticsPayload
config.load('pump_bot')
config.load_app('pump_bot')
mqtt: Optional[MqttWrapper] = None
mqtt_node: Optional[MqttNode] = None
@ -208,7 +208,7 @@ def markup(ctx: Optional[bot.Context]) -> Optional[ReplyKeyboardMarkup]:
return ReplyKeyboardMarkup(buttons, one_time_keyboard=False)
def mqtt_payload_callback(payload: MqttPayload):
def mqtt_payload_callback(mqtt_node: MqttNode, payload: MqttPayload):
global watering_mcu_status
types_the_node_can_send = (

View File

@ -13,7 +13,7 @@ from home.mqtt.module.relay import MqttRelayState
from home.mqtt.module.diagnostics import InitialDiagnosticsPayload, DiagnosticsPayload
config.load('pump_mqtt_bot')
config.load_app('pump_mqtt_bot')
bot.initialize()
bot.lang.ru(

View File

@ -6,13 +6,12 @@ from functools import partial
from home.config import config
from home.telegram import bot
from home.mqtt import MqttRelay, MqttRelayState
from home.mqtt.esp import MqttEspDevice
from home.mqtt.payload import MqttPayload
from home.mqtt.payload.relay import InitialDiagnosticsPayload, DiagnosticsPayload
from home.mqtt import MqttPayload, MqttNode, MqttWrapper
from home.mqtt.module.relay import MqttRelayModule, MqttRelayState
from home.mqtt.module.diagnostics import InitialDiagnosticsPayload, DiagnosticsPayload
config.load('relay_mqtt_bot')
config.load_app('relay_mqtt_bot')
bot.initialize()
bot.lang.ru(
@ -34,7 +33,11 @@ status_emoji = {
'on': '',
'off': ''
}
mqtt_relay: Optional[MqttRelay] = None
# mqtt_relay: Optional[MqttRelayModule] = None
mqtt: Optional[MqttWrapper] = None
relay_nodes: dict[str, MqttRelayModule] = {}
relay_states: dict[str, MqttRelayState] = {}
@ -43,23 +46,24 @@ class UserAction(Enum):
OFF = 'off'
def on_mqtt_message(home_id, message: MqttPayload):
def on_mqtt_message(node: MqttNode,
message: MqttPayload):
if isinstance(message, InitialDiagnosticsPayload) or isinstance(message, DiagnosticsPayload):
kwargs = dict(rssi=message.rssi, enabled=message.flags.state)
if isinstance(message, InitialDiagnosticsPayload):
kwargs['fw_version'] = message.fw_version
if home_id not in relay_states:
relay_states[home_id] = MqttRelayState()
relay_states[home_id].update(**kwargs)
if node.id not in relay_states:
relay_states[node.id] = MqttRelayState()
relay_states[node.id].update(**kwargs)
def enable_handler(home_id: str, ctx: bot.Context) -> None:
mqtt_relay.set_power(home_id, True)
def enable_handler(node_id: str, ctx: bot.Context) -> None:
relay_nodes[node_id].switchpower(True)
ctx.reply(ctx.lang('done'))
def disable_handler(home_id: str, ctx: bot.Context) -> None:
mqtt_relay.set_power(home_id, False)
def disable_handler(node_id: str, ctx: bot.Context) -> None:
relay_nodes[node_id].switchpower(False)
ctx.reply(ctx.lang('done'))
@ -86,9 +90,13 @@ def markup(ctx: Optional[bot.Context]) -> Optional[ReplyKeyboardMarkup]:
if __name__ == '__main__':
devices = []
mqtt = MqttWrapper()
for device_id, data in config['relays'].items():
devices.append(MqttEspDevice(id=device_id,
secret=data['secret']))
mqtt_node = MqttNode(node_id=device_id, node_secret=data['secret'])
relay_nodes[device_id] = mqtt_node.load_module('relay')
mqtt_node.add_payload_callback(on_mqtt_message)
mqtt.add_node(mqtt_node)
labels = data['labels']
bot.lang.ru(**{device_id: labels['ru']})
bot.lang.en(**{device_id: labels['en']})
@ -101,12 +109,9 @@ if __name__ == '__main__':
messages.append(f'{type_emoji}{status_emoji[action.value]} {labels[_lang]}')
bot.handler(texts=messages)(partial(enable_handler if action == UserAction.ON else disable_handler, device_id))
mqtt_relay = MqttRelay(devices=devices)
mqtt_relay.set_message_callback(on_mqtt_message)
mqtt_relay.configure_tls()
mqtt_relay.connect_and_loop(loop_forever=False)
mqtt.configure_tls()
mqtt.connect_and_loop(loop_forever=False)
# bot.enable_logging(BotType.RELAY_MQTT)
bot.run(start_handler=start)
mqtt_relay.disconnect()
mqtt.disconnect()

View File

@ -1,17 +1,19 @@
#!/usr/bin/env python3
from home import http
from home.config import config
from home.mqtt import MqttRelay, MqttRelayState
from home.mqtt.esp import MqttEspDevice
from home.mqtt.payload import MqttPayload
from home.mqtt.payload.relay import InitialDiagnosticsPayload, DiagnosticsPayload
from typing import Optional
from home.mqtt import MqttPayload, MqttWrapper, MqttNode, MqttModule
from home.mqtt.module.relay import MqttRelayState, MqttRelayModule
from home.mqtt.module.diagnostics import InitialDiagnosticsPayload, DiagnosticsPayload
from typing import Optional, Union
mqtt_relay: Optional[MqttRelay] = None
mqtt: Optional[MqttWrapper] = None
mqtt_nodes: dict[str, MqttNode] = {}
relay_modules: dict[str, Union[MqttRelayModule, MqttModule]] = {}
relay_states: dict[str, MqttRelayState] = {}
def on_mqtt_message(device_id, message: MqttPayload):
def on_mqtt_message(node: MqttNode,
message: MqttPayload):
if isinstance(message, InitialDiagnosticsPayload) or isinstance(message, DiagnosticsPayload):
kwargs = dict(rssi=message.rssi, enabled=message.flags.state)
if device_id not in relay_states:
@ -29,17 +31,22 @@ class RelayMqttHttpProxy(http.HTTPServer):
async def _relay_on_off(self,
enable: Optional[bool],
req: http.Request):
device_id = req.match_info['id']
device_secret = req.query['secret']
node_id = req.match_info['id']
node_secret = req.query['secret']
node = mqtt_nodes[node_id]
relay_module = relay_modules[node_id]
if enable is None:
if device_id in relay_states and relay_states[device_id].ever_updated:
cur_state = relay_states[device_id].enabled
if node_id in relay_states and relay_states[node_id].ever_updated:
cur_state = relay_states[node_id].enabled
else:
cur_state = False
enable = not cur_state
mqtt_relay.set_power(device_id, enable, device_secret)
if not node.secret:
node.secret = node_secret
relay_module.switchpower(enable)
return self.ok()
async def relay_on(self, req: http.Request):
@ -53,15 +60,22 @@ class RelayMqttHttpProxy(http.HTTPServer):
if __name__ == '__main__':
config.load('relay_mqtt_http_proxy')
config.load_app('relay_mqtt_http_proxy')
mqtt_relay = MqttRelay(devices=[MqttEspDevice(id=device_id) for device_id in config.get('relay.devices')])
mqtt_relay.configure_tls()
mqtt_relay.set_message_callback(on_mqtt_message)
mqtt_relay.connect_and_loop(loop_forever=False)
mqtt = MqttWrapper()
for device_id, data in config['relays'].items():
mqtt_node = MqttNode(node_id=device_id)
relay_modules[device_id] = mqtt_node.load_module('relay')
mqtt_nodes[device_id] = mqtt_node
mqtt_node.add_payload_callback(on_mqtt_message)
mqtt.add_node(mqtt_node)
mqtt_node.add_payload_callback(on_mqtt_message)
mqtt.configure_tls()
mqtt.connect_and_loop(loop_forever=False)
proxy = RelayMqttHttpProxy(config.get_addr('server.listen'))
try:
proxy.run()
except KeyboardInterrupt:
mqtt_relay.disconnect()
mqtt.disconnect()

View File

@ -23,7 +23,7 @@ from home.api.types import (
TemperatureSensorLocation
)
config.load('sensors_bot')
config.load_app('sensors_bot')
bot.initialize()
bot.lang.ru(

View File

@ -1,58 +0,0 @@
#!/usr/bin/env python3
import time
import json
from home.util import parse_addr, MySimpleSocketClient
from home.mqtt import Mqtt, poll_tick
from home.mqtt.payload.sensors import Temperature
from home.config import config
class MqttClient(Mqtt):
def __init__(self):
super().__init__(self)
self._home_id = config['mqtt']['home_id']
def poll(self):
freq = int(config['mqtt']['sensors']['poll_freq'])
self._logger.debug(f'freq={freq}')
g = poll_tick(freq)
while True:
time.sleep(next(g))
for k, v in config['mqtt']['sensors']['si7021'].items():
host, port = parse_addr(v['addr'])
self.publish_si7021(host, port, k)
def publish_si7021(self, host: str, port: int, name: str):
self._logger.debug(f"publish_si7021/{name}: {host}:{port}")
try:
now = time.time()
socket = MySimpleSocketClient(host, port)
socket.write('read')
response = json.loads(socket.read().strip())
temp = response['temp']
humidity = response['humidity']
self._logger.debug(f'publish_si7021/{name}: temp={temp} humidity={humidity}')
pld = Temperature(time=round(now),
temp=temp,
rh=humidity)
self._client.publish(f'hk/{self._home_id}/si7021/{name}',
payload=pld.pack(),
qos=1)
except Exception as e:
self._logger.exception(e)
if __name__ == '__main__':
config.load('sensors_mqtt_sender')
client = MqttClient()
client.configure_tls()
client.connect_and_loop(loop_forever=False)
client.poll()

View File

@ -23,7 +23,7 @@ from telegram import ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardBu
from PIL import Image
config.load('sound_bot')
config.load_app('sound_bot')
nodes = {}
for nodename, nodecfg in config['nodes'].items():

View File

@ -77,7 +77,7 @@ if __name__ == '__main__':
if not os.getegid() == 0:
raise RuntimeError("Must be run as root.")
config.load('sound_node')
config.load_app('sound_node')
storage = SoundRecordStorage(config['node']['storage'])

View File

@ -14,7 +14,7 @@ if __name__ == '__main__':
if not os.getegid() == 0:
sys.exit('Must be run as root.')
config.load('sound_sensor_node')
config.load_app('sound_sensor_node')
kwargs = {}
if 'delay' in config['node']:

View File

@ -159,7 +159,7 @@ def api_error_handler(exc, name, req: RequestParams):
if __name__ == '__main__':
config.load('sound_sensor_server')
config.load_app('sound_sensor_server')
hc = HitCounter()
api = WebAPIClient(timeout=(10, 60))

View File

@ -3,7 +3,7 @@
from home.config import config
if __name__ == '__main__':
config.load('ssh_tunnels_config_util')
config.load_app('ssh_tunnels_config_util')
network_prefix = config['network']
hostnames = []

View File

@ -63,7 +63,7 @@ async def run_server(host, port):
if __name__ == '__main__':
config.load()
config.load_app()
if 'measure_delay' in config['sensor']:
delay = float(config['sensor']['measure_delay'])

View File

@ -2,18 +2,8 @@
import paho.mqtt.client as mqtt
import re
from home.mqtt import Mqtt
from home.config import config
from home.mqtt.payload.sensors import Temperature
from home.api.types import TemperatureSensorLocation
from home.database import SensorsDatabase
def get_sensor_type(sensor: str) -> TemperatureSensorLocation:
for item in TemperatureSensorLocation:
if sensor == item.name.lower():
return item
raise ValueError(f'unexpected sensor value: {sensor}')
from home.mqtt import MqttWrapper, MqttNode
class MqttServer(Mqtt):
@ -47,7 +37,12 @@ class MqttServer(Mqtt):
if __name__ == '__main__':
config.load('sensors_mqtt_receiver')
config.load_app('temphum_mqtt_receiver')
server = MqttServer()
server.connect_and_loop()
mqtt = MqttWrapper(clean_session=False)
node = MqttNode(node_id='+')
node.load_module('temphum', write_to_database=True)
mqtt.add_node(node)
mqtt.configure_tls()
mqtt.connect_and_loop()

View File

@ -63,7 +63,7 @@ async def run_server(host, port):
if __name__ == '__main__':
config.load()
config.load_app()
if 'measure_delay' in config['sensor']:
delay = float(config['sensor']['measure_delay'])

9
src/test_new_config.py Normal file
View File

@ -0,0 +1,9 @@
from home.config import config, app_config, LinuxBoardsConfig
from pprint import pprint
if __name__ == '__main__':
config.load_app(name=False)
lbc = LinuxBoardsConfig()
pprint(lbc.data)

View File

@ -231,7 +231,7 @@ if __name__ == '__main__':
_app_name = 'web_api'
if is_development_mode():
_app_name += '_dev'
config.load(_app_name)
config.load_app(_app_name)
loop = asyncio.get_event_loop()

View File

@ -1,12 +1,12 @@
[Unit]
Description=sensors mqtt receiver
Description=temphum mqtt receiver
After=network.target
[Service]
User=user
Group=user
Restart=on-failure
ExecStart=python3 /home/user/home/src/sensors_mqtt_receiver.py
ExecStart=python3 /home/user/home/src/temphum_mqtt_receiver.py
WorkingDirectory=/home/user
[Install]

View File

@ -1,13 +0,0 @@
[Unit]
Description=Sensors MQTT sender
After=temphumd.service
[Service]
User=user
Group=user
Restart=on-failure
ExecStart=/home/user/homekit/src/sensors_mqtt_sender.py
WorkingDirectory=/home/user
[Install]
WantedBy=multi-user.target

View File

@ -12,7 +12,7 @@ from src.home.mqtt.relay import MQTTRelayClient
if __name__ == '__main__':
config.load('test_mqtt_relay_server')
config.load_app('test_mqtt_relay_server')
relay = MQTTRelayClient('test')
relay.configure_tls()
relay.connect_and_loop()

View File

@ -18,7 +18,7 @@ if __name__ == '__main__':
parser.add_argument('--off', action='store_true')
parser.add_argument('--stat', action='store_true')
config.load('test_mqtt_relay', parser=parser)
config.load_app('test_mqtt_relay', parser=parser)
arg = parser.parse_args()
relay = MQTTRelayController('test')

View File

@ -28,7 +28,7 @@ if __name__ == '__main__':
parser.add_argument('--decr', type=str)
# parser.add_argument('--dump-config', action='store_true')
args = config.load('test_amixer', parser=parser)
args = config.load_app('test_amixer', parser=parser)
# if args.dump_config:
# print(config.data)

View File

@ -13,7 +13,7 @@ from src.home.config import config
if __name__ == '__main__':
config.load('test_api')
config.load_app('test_api')
api = WebAPIClient()
print(api.log_bot_request(BotType.ADMIN, 1, "test_api.py"))

View File

@ -21,7 +21,7 @@ if __name__ == '__main__':
parser.add_argument('--status', action='store_true',
help='print status and exit')
arg = config.load(False, parser=parser)
arg = config.load_app(False, parser=parser)
cam = esp32.WebClient(addr=parse_addr(arg.addr))
if arg.status:

View File

@ -372,5 +372,5 @@ def main():
if __name__ == '__main__':
config.load('test_inverter_monitor')
config.load_app('test_inverter_monitor')
main()

View File

@ -77,5 +77,5 @@ def cleanup_job():
if __name__ == '__main__':
config.load('ipcam_server')
config.load_app('ipcam_server')
cleanup_job()

View File

@ -64,7 +64,7 @@ def api_success_handler(response, name, req: RequestParams):
if __name__ == '__main__':
config.load('test_record_upload')
config.load_app('test_record_upload')
nodes = {}
for name, addr in config['nodes'].items():

View File

@ -56,7 +56,7 @@ def hits_sender():
if __name__ == '__main__':
config.load('test_api')
config.load_app('test_api')
hc = HitCounter()
api = WebAPIClient()

View File

@ -20,7 +20,7 @@ async def main():
if __name__ == '__main__':
config.load('test_telegram_aio_send_photo')
config.load_app('test_telegram_aio_send_photo')
loop = asyncio.get_event_loop()
asyncio.ensure_future(main())