-
Notifications
You must be signed in to change notification settings - Fork 0
/
nhc_hass.py
executable file
·158 lines (139 loc) · 5.52 KB
/
nhc_hass.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env python3
import sys
import os
import logging
import signal
import time
from traceback import format_exc
from argparse import ArgumentParser
from daemon import DaemonContext
from lockfile.pidlockfile import PIDLockFile
from nhc.hobby_api import hobbyAPI, controlNHC
from hass.mqtt import Hass
from lib.bridge_prompt import prompt
from lib.mylogger import mylogger
import subprocess
from subprocess import PIPE, run
class Application():
def __init__(self, options, clilogger):
self.nhcconfig = options.nhcconfig
self.clilogger = clilogger
self.logger = clilogger.get_logger()
self.hobby = None
self.hass = None
self.running = False
def run(self, foreground=False):
while True:
try:
self.logger.info("NHC Hass Bridge started")
self.hobby = hobbyAPI(self.logger, self.nhcconfig)
self.nhccontrol = controlNHC(self.hobby)
self.hass = Hass(self.logger, hobby=self.hobby)
self.hobby.start()
self.hass.start()
self.hobby.set_callbacks(self.hass.nhc_status_update, self.hass.nhc_remove_device, self.hass.nhc_add_device)
# give some time to connect
time.sleep(3)
self.running = self.overall_status()
# start infinite while loop
if foreground:
app = prompt(self.clilogger, self.nhccontrol, self.hass)
sys.exit(app.cmdloop())
else:
while self.running:
self.running = self.overall_status()
time.sleep(1)
return 0
except Exception as exc:
self.logger.info("%s", format_exc())
self.logger.fatal("%s", exc)
if foreground:
return 1
time.sleep(15)
def shutdown(self, signum, frame):
self.logger.info("Shutting down with signal %s", signal.Signals(signum).name)
if self.hobby is not None:
self.hobby.stop()
if self.hass is not None:
self.hass.stop()
def overall_status(self):
status_hobby = self.hobby.is_connected()
status_hass = self.hass.is_connected()
if status_hobby and status_hass:
return True
else:
self.logger.error("Fault status: hobby:%d, hass:%d", status_hobby, status_hass)
return False
class CreateApp(object):
def __init__(self, options):
self.clilogger = mylogger("nhchabridge", options.loglevel)
self.logger = self.clilogger.get_logger()
self.options = options
if os.access("/var/run/", os.W_OK):
_pidfile = "/var/run/nhchabridge.pid"
else:
_pidfile = "/var/tmp/nhchabridge.pid"
self.pid_file = PIDLockFile(_pidfile)
self.check_stale_pid()
def check_stale_pid(self):
_pid = self.pid_file.read_pid()
if _pid is None:
return
try:
os.kill(_pid, 0)
except OSError:
# corresponding process doesn't exist
self.pid_file.break_lock()
else:
# process exist, handled further
pass
def stop_daemon(self):
if self.pid_file.is_locked():
print("Stopping service with pid", self.pid_file.read_pid())
os.kill(self.pid_file.read_pid(), signal.SIGTERM)
def start_foreground(self):
self.clilogger.set_logger_stream()
if self.pid_file.is_locked():
self.logger.warn("Attention, service already running with pid %d, ", self.pid_file.read_pid())
else:
self.pid_file.acquire()
daemon = Application(self.options, self.clilogger)
try:
daemon.run(foreground=True)
except (SystemExit, KeyboardInterrupt):
daemon.shutdown(2, None)
def start_daemon(self):
self.clilogger.set_logger_syslog()
if self.pid_file.is_locked():
self.logger.error("Service already running with pid %d", self.pid_file.read_pid())
return
daemon = Application(self.options, self.clilogger)
context = DaemonContext()
context.pidfile = self.pid_file
context.files_preserve = [self.clilogger.get_syslog_fileno()]
context.signal_map = {signal.SIGTERM: daemon.shutdown, signal.SIGINT: daemon.shutdown}
with context:
self.logger.info("Starting service with pid %d", self.pid_file.read_pid())
try:
daemon.run(foreground=False)
except SystemExit:
daemon.shutdown(2, None)
def main(options):
app = CreateApp(options=options)
if options.kill:
app.stop_daemon()
return
if options.nhcconfig is None:
print("Missing NHC configuration file in arguments")
return
elif options.foreground:
app.start_foreground()
else:
app.start_daemon()
if __name__ == '__main__':
parser = ArgumentParser(description="NHC Hass bridge")
parser.add_argument('-l', '--loglevel', help='Loglevel: d(ebug), i(nfo), w(arning), e(rror)', choices=['d','i','w','e'], default="e")
parser.add_argument('-n', '--nhcconfig', help='NHC configuration file', default="./nhc.yaml")
parser.add_argument('-f', '--foreground', help='Run in foreground', default=False, action='store_true')
parser.add_argument('-k', '--kill', help='Kill running daemon', default=False, action='store_true')
main(parser.parse_args())