save
This commit is contained in:
parent
5aad97192d
commit
3ae1c3b5a7
@ -14,7 +14,7 @@ apscheduler~=3.9.1
|
|||||||
psutil~=5.9.1
|
psutil~=5.9.1
|
||||||
aioshutil~=1.1
|
aioshutil~=1.1
|
||||||
scikit-image~=0.19.3
|
scikit-image~=0.19.3
|
||||||
|
cerberus~=1.3.4
|
||||||
# following can be installed from debian repositories
|
# following can be installed from debian repositories
|
||||||
# matplotlib~=3.5.0
|
# matplotlib~=3.5.0
|
||||||
|
|
||||||
|
46
src/home/config/_validators.py
Normal file
46
src/home/config/_validators.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import logging
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from cerberus import Validator, DocumentError
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'linux_boards_validator'
|
||||||
|
]
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
@ -7,39 +7,50 @@ from os.path import join, isdir, isfile
|
|||||||
from typing import Optional, Any, MutableMapping
|
from typing import Optional, Any, MutableMapping
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from ..util import parse_addr
|
from ..util import parse_addr
|
||||||
|
import _validators as validators
|
||||||
|
|
||||||
|
|
||||||
|
_validators = {}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_validator(name: str) -> Optional[callable]:
|
||||||
|
if hasattr(validators, f'{name}_validator'):
|
||||||
|
return getattr(validators, f'{name}_validator')
|
||||||
|
if name in _validators:
|
||||||
|
return _validators[name]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def add_validator(name: str, f: callable):
|
||||||
|
_validators[name] = f
|
||||||
|
|
||||||
|
|
||||||
def _get_config_path(name: str) -> str:
|
def _get_config_path(name: str) -> str:
|
||||||
formats = ['toml', 'yaml']
|
formats = ['toml', 'yaml']
|
||||||
|
|
||||||
dirname = join(os.environ['HOME'], '.config', name)
|
dirnames = [
|
||||||
|
join(os.environ['HOME'], '.config', 'homekit'),
|
||||||
|
'/etc/homekit'
|
||||||
|
]
|
||||||
|
|
||||||
if isdir(dirname):
|
for dirname in dirnames:
|
||||||
for fmt in formats:
|
if isdir(dirname):
|
||||||
filename = join(dirname, f'config.{fmt}')
|
for fmt in formats:
|
||||||
if isfile(filename):
|
filename = join(dirname, f'{name}.{fmt}')
|
||||||
return filename
|
if isfile(filename):
|
||||||
|
return filename
|
||||||
|
|
||||||
raise IOError(f'config not found in {dirname}')
|
raise IOError(f'config \'{name}\' not found')
|
||||||
|
|
||||||
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')
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigStore:
|
class SingleConfig:
|
||||||
data: MutableMapping[str, Any]
|
data: MutableMapping[str, Any]
|
||||||
app_name: Optional[str]
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.data = {}
|
self.data = {}
|
||||||
self.app_name = None
|
|
||||||
|
|
||||||
def load(self, name: Optional[str] = None,
|
def load(self,
|
||||||
|
name: Optional[str] = None,
|
||||||
use_cli=True,
|
use_cli=True,
|
||||||
parser: ArgumentParser = None):
|
parser: ArgumentParser = None):
|
||||||
self.app_name = name
|
self.app_name = name
|
||||||
@ -126,6 +137,16 @@ class ConfigStore:
|
|||||||
return self.data.items()
|
return self.data.items()
|
||||||
|
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
app_name: Optional[str]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.app_name = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
config = ConfigStore()
|
config = ConfigStore()
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,13 +14,17 @@ class MqttNode:
|
|||||||
_modules: List[MqttModule]
|
_modules: List[MqttModule]
|
||||||
_module_subscriptions: dict[str, MqttModule]
|
_module_subscriptions: dict[str, MqttModule]
|
||||||
_node_id: str
|
_node_id: str
|
||||||
|
_node_secret: str
|
||||||
_payload_callbacks: list[callable]
|
_payload_callbacks: list[callable]
|
||||||
_wrapper: Optional[MqttWrapper]
|
_wrapper: Optional[MqttWrapper]
|
||||||
|
|
||||||
def __init__(self, node_id: str):
|
def __init__(self,
|
||||||
|
node_id: str,
|
||||||
|
node_secret: Optional[str] = None):
|
||||||
self._modules = []
|
self._modules = []
|
||||||
self._module_subscriptions = {}
|
self._module_subscriptions = {}
|
||||||
self._node_id = node_id
|
self._node_id = node_id
|
||||||
|
self._node_secret = node_secret
|
||||||
self._payload_callbacks = []
|
self._payload_callbacks = []
|
||||||
self._logger = logging.getLogger(self.__class__.__name__)
|
self._logger = logging.getLogger(self.__class__.__name__)
|
||||||
self._wrapper = None
|
self._wrapper = None
|
||||||
@ -42,7 +46,7 @@ class MqttNode:
|
|||||||
payload = self._module_subscriptions[topic].handle_payload(self, topic, payload)
|
payload = self._module_subscriptions[topic].handle_payload(self, topic, payload)
|
||||||
if isinstance(payload, MqttPayload):
|
if isinstance(payload, MqttPayload):
|
||||||
for f in self._payload_callbacks:
|
for f in self._payload_callbacks:
|
||||||
f(payload)
|
f(self, payload)
|
||||||
|
|
||||||
def load_module(self, module_name: str, *args, **kwargs) -> MqttModule:
|
def load_module(self, module_name: str, *args, **kwargs) -> MqttModule:
|
||||||
module = importlib.import_module(f'..module.{module_name}', __name__)
|
module = importlib.import_module(f'..module.{module_name}', __name__)
|
||||||
@ -78,3 +82,11 @@ class MqttNode:
|
|||||||
@property
|
@property
|
||||||
def id(self) -> str:
|
def id(self) -> str:
|
||||||
return self._node_id
|
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
|
||||||
|
@ -9,11 +9,15 @@ from ..util import strgen
|
|||||||
class MqttWrapper(Mqtt):
|
class MqttWrapper(Mqtt):
|
||||||
_nodes: list[MqttNode]
|
_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']
|
client_id = config['mqtt']['client_id']
|
||||||
if randomize_client_id:
|
if randomize_client_id:
|
||||||
client_id += '_'+strgen(6)
|
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._nodes = []
|
||||||
self._topic_prefix = topic_prefix
|
self._topic_prefix = topic_prefix
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class OtaPayload(MqttPayload):
|
|||||||
|
|
||||||
|
|
||||||
class MqttOtaModule(MqttModule):
|
class MqttOtaModule(MqttModule):
|
||||||
_ota_request: Optional[tuple[str, str, int]]
|
_ota_request: Optional[tuple[str, int]]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -52,9 +52,9 @@ class MqttOtaModule(MqttModule):
|
|||||||
mqtt.subscribe_module("otares", self)
|
mqtt.subscribe_module("otares", self)
|
||||||
|
|
||||||
if self._ota_request is not None:
|
if self._ota_request is not None:
|
||||||
secret, filename, qos = self._ota_request
|
filename, qos = self._ota_request
|
||||||
self._ota_request = None
|
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]:
|
def handle_payload(self, mqtt: MqttNode, topic: str, payload: bytes) -> Optional[MqttPayload]:
|
||||||
if topic == 'otares':
|
if topic == 'otares':
|
||||||
@ -69,10 +69,9 @@ class MqttOtaModule(MqttModule):
|
|||||||
qos=qos)
|
qos=qos)
|
||||||
|
|
||||||
def push_ota(self,
|
def push_ota(self,
|
||||||
secret: str,
|
|
||||||
filename: str,
|
filename: str,
|
||||||
qos: int):
|
qos: int):
|
||||||
if not self._initialized:
|
if not self._initialized:
|
||||||
self._ota_request = (secret, filename, qos)
|
self._ota_request = (filename, qos)
|
||||||
else:
|
else:
|
||||||
self.do_push_ota(secret, filename, qos)
|
self.do_push_ota(filename, qos)
|
||||||
|
@ -64,9 +64,9 @@ class MqttRelayModule(MqttModule):
|
|||||||
mqtt.subscribe_module('relay/status', self)
|
mqtt.subscribe_module('relay/status', self)
|
||||||
|
|
||||||
def switchpower(self,
|
def switchpower(self,
|
||||||
enable: bool,
|
enable: bool):
|
||||||
secret: str):
|
payload = MqttPowerSwitchPayload(secret=self._mqtt_node_ref.secret,
|
||||||
payload = MqttPowerSwitchPayload(secret=secret, state=enable)
|
state=enable)
|
||||||
self._mqtt_node_ref.publish('relay/switch', payload=payload.pack())
|
self._mqtt_node_ref.publish('relay/switch', payload=payload.pack())
|
||||||
|
|
||||||
def handle_payload(self, mqtt: MqttNode, topic: str, payload: bytes) -> Optional[MqttPayload]:
|
def handle_payload(self, mqtt: MqttNode, topic: str, payload: bytes) -> Optional[MqttPayload]:
|
||||||
|
@ -48,6 +48,7 @@ class MqttTemphumDataPayload(MqttPayload):
|
|||||||
class MqttTempHumModule(MqttModule):
|
class MqttTempHumModule(MqttModule):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
sensor: Optional[BaseSensor] = None,
|
sensor: Optional[BaseSensor] = None,
|
||||||
|
write_to_database=False,
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
if sensor is not None:
|
if sensor is not None:
|
||||||
kwargs['tick_interval'] = 10
|
kwargs['tick_interval'] = 10
|
||||||
|
@ -11,7 +11,7 @@ if __name__ == '__main__':
|
|||||||
config.load('inverter_mqtt_util', parser=parser)
|
config.load('inverter_mqtt_util', parser=parser)
|
||||||
arg = parser.parse_args()
|
arg = parser.parse_args()
|
||||||
|
|
||||||
mqtt = MqttWrapper()
|
mqtt = MqttWrapper(clean_session=arg.mode[0] != 'receiver')
|
||||||
node = MqttNode(node_id='inverter')
|
node = MqttNode(node_id='inverter')
|
||||||
module_kwargs = {}
|
module_kwargs = {}
|
||||||
if arg.mode[0] == 'sender':
|
if arg.mode[0] == 'sender':
|
||||||
|
@ -30,7 +30,7 @@ if __name__ == '__main__':
|
|||||||
raise ArgumentError(None, '--relay is only allowed when \'relay\' module included in --modules')
|
raise ArgumentError(None, '--relay is only allowed when \'relay\' module included in --modules')
|
||||||
|
|
||||||
mqtt = MqttWrapper(randomize_client_id=True)
|
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)
|
mqtt.add_node(mqtt_node)
|
||||||
|
|
||||||
@ -44,9 +44,7 @@ if __name__ == '__main__':
|
|||||||
if m == 'relay' and arg.switch_relay is not None:
|
if m == 'relay' and arg.switch_relay is not None:
|
||||||
if not arg.node_secret:
|
if not arg.node_secret:
|
||||||
raise ArgumentError(None, '--switch-relay requires --node-secret')
|
raise ArgumentError(None, '--switch-relay requires --node-secret')
|
||||||
module_instance.switchpower(mqtt_node,
|
module_instance.switchpower(arg.switch_relay == 1)
|
||||||
arg.switch_relay == 1,
|
|
||||||
arg.node_secret)
|
|
||||||
|
|
||||||
mqtt.configure_tls()
|
mqtt.configure_tls()
|
||||||
try:
|
try:
|
||||||
@ -58,7 +56,7 @@ if __name__ == '__main__':
|
|||||||
if not arg.node_secret:
|
if not arg.node_secret:
|
||||||
raise ArgumentError(None, 'pushing OTA requires --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:
|
while True:
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
|
@ -208,7 +208,7 @@ def markup(ctx: Optional[bot.Context]) -> Optional[ReplyKeyboardMarkup]:
|
|||||||
return ReplyKeyboardMarkup(buttons, one_time_keyboard=False)
|
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
|
global watering_mcu_status
|
||||||
|
|
||||||
types_the_node_can_send = (
|
types_the_node_can_send = (
|
||||||
|
@ -6,10 +6,9 @@ from functools import partial
|
|||||||
|
|
||||||
from home.config import config
|
from home.config import config
|
||||||
from home.telegram import bot
|
from home.telegram import bot
|
||||||
from home.mqtt import MqttRelay, MqttRelayState
|
from home.mqtt import MqttPayload, MqttNode, MqttWrapper
|
||||||
from home.mqtt.esp import MqttEspDevice
|
from home.mqtt.module.relay import MqttRelayModule, MqttRelayState
|
||||||
from home.mqtt.payload import MqttPayload
|
from home.mqtt.module.diagnostics import InitialDiagnosticsPayload, DiagnosticsPayload
|
||||||
from home.mqtt.payload.relay import InitialDiagnosticsPayload, DiagnosticsPayload
|
|
||||||
|
|
||||||
|
|
||||||
config.load('relay_mqtt_bot')
|
config.load('relay_mqtt_bot')
|
||||||
@ -36,7 +35,9 @@ status_emoji = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mqtt_relay: Optional[MqttRelay] = None
|
# mqtt_relay: Optional[MqttRelayModule] = None
|
||||||
|
mqtt: Optional[MqttWrapper] = None
|
||||||
|
relay_nodes: dict[str, MqttRelayModule] = {}
|
||||||
relay_states: dict[str, MqttRelayState] = {}
|
relay_states: dict[str, MqttRelayState] = {}
|
||||||
|
|
||||||
|
|
||||||
@ -45,23 +46,24 @@ class UserAction(Enum):
|
|||||||
OFF = 'off'
|
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):
|
if isinstance(message, InitialDiagnosticsPayload) or isinstance(message, DiagnosticsPayload):
|
||||||
kwargs = dict(rssi=message.rssi, enabled=message.flags.state)
|
kwargs = dict(rssi=message.rssi, enabled=message.flags.state)
|
||||||
if isinstance(message, InitialDiagnosticsPayload):
|
if isinstance(message, InitialDiagnosticsPayload):
|
||||||
kwargs['fw_version'] = message.fw_version
|
kwargs['fw_version'] = message.fw_version
|
||||||
if home_id not in relay_states:
|
if node.id not in relay_states:
|
||||||
relay_states[home_id] = MqttRelayState()
|
relay_states[node.id] = MqttRelayState()
|
||||||
relay_states[home_id].update(**kwargs)
|
relay_states[node.id].update(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def enable_handler(home_id: str, ctx: bot.Context) -> None:
|
def enable_handler(node_id: str, ctx: bot.Context) -> None:
|
||||||
mqtt_relay.set_power(home_id, True)
|
relay_nodes[node_id].switchpower(True)
|
||||||
ctx.reply(ctx.lang('done'))
|
ctx.reply(ctx.lang('done'))
|
||||||
|
|
||||||
|
|
||||||
def disable_handler(home_id: str, ctx: bot.Context) -> None:
|
def disable_handler(node_id: str, ctx: bot.Context) -> None:
|
||||||
mqtt_relay.set_power(home_id, False)
|
relay_nodes[node_id].switchpower(False)
|
||||||
ctx.reply(ctx.lang('done'))
|
ctx.reply(ctx.lang('done'))
|
||||||
|
|
||||||
|
|
||||||
@ -88,9 +90,13 @@ def markup(ctx: Optional[bot.Context]) -> Optional[ReplyKeyboardMarkup]:
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
devices = []
|
devices = []
|
||||||
|
mqtt = MqttWrapper()
|
||||||
for device_id, data in config['relays'].items():
|
for device_id, data in config['relays'].items():
|
||||||
devices.append(MqttEspDevice(id=device_id,
|
mqtt_node = MqttNode(node_id=device_id, node_secret=data['secret'])
|
||||||
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']
|
labels = data['labels']
|
||||||
bot.lang.ru(**{device_id: labels['ru']})
|
bot.lang.ru(**{device_id: labels['ru']})
|
||||||
bot.lang.en(**{device_id: labels['en']})
|
bot.lang.en(**{device_id: labels['en']})
|
||||||
@ -103,12 +109,9 @@ if __name__ == '__main__':
|
|||||||
messages.append(f'{type_emoji}{status_emoji[action.value]} {labels[_lang]}')
|
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))
|
bot.handler(texts=messages)(partial(enable_handler if action == UserAction.ON else disable_handler, device_id))
|
||||||
|
|
||||||
mqtt_relay = MqttRelay(devices=devices)
|
mqtt.configure_tls()
|
||||||
mqtt_relay.set_message_callback(on_mqtt_message)
|
mqtt.connect_and_loop(loop_forever=False)
|
||||||
mqtt_relay.configure_tls()
|
|
||||||
mqtt_relay.connect_and_loop(loop_forever=False)
|
|
||||||
|
|
||||||
# bot.enable_logging(BotType.RELAY_MQTT)
|
|
||||||
bot.run(start_handler=start)
|
bot.run(start_handler=start)
|
||||||
|
|
||||||
mqtt_relay.disconnect()
|
mqtt.disconnect()
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from home import http
|
from home import http
|
||||||
from home.config import config
|
from home.config import config
|
||||||
from home.mqtt import MqttRelay, MqttRelayState
|
from home.mqtt import MqttPayload, MqttWrapper, MqttNode
|
||||||
from home.mqtt import MqttPayload
|
from home.mqtt.module.relay import MqttRelayState, MqttRelayModule
|
||||||
from home.mqtt.module.diagnostics import InitialDiagnosticsPayload, DiagnosticsPayload
|
from home.mqtt.module.diagnostics import InitialDiagnosticsPayload, DiagnosticsPayload
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
mqtt_relay: Optional[MqttRelay] = None
|
mqtt: Optional[MqttWrapper] = None
|
||||||
|
mqtt_nodes: dict[str, MqttNode] = {}
|
||||||
|
relay_modules: dict[str, MqttRelayModule] = {}
|
||||||
relay_states: dict[str, MqttRelayState] = {}
|
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):
|
if isinstance(message, InitialDiagnosticsPayload) or isinstance(message, DiagnosticsPayload):
|
||||||
kwargs = dict(rssi=message.rssi, enabled=message.flags.state)
|
kwargs = dict(rssi=message.rssi, enabled=message.flags.state)
|
||||||
if device_id not in relay_states:
|
if device_id not in relay_states:
|
||||||
@ -28,17 +31,22 @@ class RelayMqttHttpProxy(http.HTTPServer):
|
|||||||
async def _relay_on_off(self,
|
async def _relay_on_off(self,
|
||||||
enable: Optional[bool],
|
enable: Optional[bool],
|
||||||
req: http.Request):
|
req: http.Request):
|
||||||
device_id = req.match_info['id']
|
node_id = req.match_info['id']
|
||||||
device_secret = req.query['secret']
|
node_secret = req.query['secret']
|
||||||
|
|
||||||
|
node = mqtt_nodes[node_id]
|
||||||
|
relay_module = relay_modules[node_id]
|
||||||
|
|
||||||
if enable is None:
|
if enable is None:
|
||||||
if device_id in relay_states and relay_states[device_id].ever_updated:
|
if node_id in relay_states and relay_states[node_id].ever_updated:
|
||||||
cur_state = relay_states[device_id].enabled
|
cur_state = relay_states[node_id].enabled
|
||||||
else:
|
else:
|
||||||
cur_state = False
|
cur_state = False
|
||||||
enable = not cur_state
|
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()
|
return self.ok()
|
||||||
|
|
||||||
async def relay_on(self, req: http.Request):
|
async def relay_on(self, req: http.Request):
|
||||||
@ -54,13 +62,20 @@ class RelayMqttHttpProxy(http.HTTPServer):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
config.load('relay_mqtt_http_proxy')
|
config.load('relay_mqtt_http_proxy')
|
||||||
|
|
||||||
mqtt_relay = MqttRelay(devices=[MqttEspDevice(id=device_id) for device_id in config.get('relay.devices')])
|
mqtt = MqttWrapper()
|
||||||
mqtt_relay.configure_tls()
|
for device_id, data in config['relays'].items():
|
||||||
mqtt_relay.set_message_callback(on_mqtt_message)
|
mqtt_node = MqttNode(node_id=device_id)
|
||||||
mqtt_relay.connect_and_loop(loop_forever=False)
|
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'))
|
proxy = RelayMqttHttpProxy(config.get_addr('server.listen'))
|
||||||
try:
|
try:
|
||||||
proxy.run()
|
proxy.run()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
mqtt_relay.disconnect()
|
mqtt.disconnect()
|
||||||
|
@ -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()
|
|
@ -2,18 +2,8 @@
|
|||||||
import paho.mqtt.client as mqtt
|
import paho.mqtt.client as mqtt
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from home.mqtt import Mqtt
|
|
||||||
from home.config import config
|
from home.config import config
|
||||||
from home.mqtt.payload.sensors import Temperature
|
from home.mqtt import MqttWrapper, MqttNode
|
||||||
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}')
|
|
||||||
|
|
||||||
|
|
||||||
class MqttServer(Mqtt):
|
class MqttServer(Mqtt):
|
||||||
@ -47,7 +37,12 @@ class MqttServer(Mqtt):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
config.load('sensors_mqtt_receiver')
|
config.load('temphum_mqtt_receiver')
|
||||||
|
|
||||||
server = MqttServer()
|
mqtt = MqttWrapper(clean_session=False)
|
||||||
server.connect_and_loop()
|
node = MqttNode(node_id='+')
|
||||||
|
node.load_module('temphum', write_to_database=True)
|
||||||
|
mqtt.add_node(node)
|
||||||
|
|
||||||
|
mqtt.configure_tls()
|
||||||
|
mqtt.connect_and_loop()
|
@ -1,12 +1,12 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=sensors mqtt receiver
|
Description=temphum mqtt receiver
|
||||||
After=network.target
|
After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
User=user
|
User=user
|
||||||
Group=user
|
Group=user
|
||||||
Restart=on-failure
|
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
|
WorkingDirectory=/home/user
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
@ -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
|
|
Loading…
x
Reference in New Issue
Block a user