Smarthome Functionen
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

devices.py 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. import colored
  2. import copy
  3. import json
  4. import task
  5. import time
  6. COLOR_GUI_ACTIVE = colored.fg("light_blue")
  7. COLOR_GUI_PASSIVE = COLOR_GUI_ACTIVE + colored.attr("dim")
  8. COLOR_SHELLY = colored.fg("light_magenta")
  9. COLOR_POWERPLUG = colored.fg("light_cyan")
  10. COLOR_LIGHT_ACTIVE = colored.fg("yellow")
  11. COLOR_LIGHT_PASSIVE = COLOR_LIGHT_ACTIVE + colored.attr("dim")
  12. COLOR_MOTION_SENSOR = colored.fg("dark_orange_3b")
  13. COLOR_HEATING_VALVE = colored.fg("red")
  14. COLOR_REMOTE = colored.fg("green")
  15. def payload_filter(payload):
  16. try:
  17. return json.loads(payload)
  18. except json.decoder.JSONDecodeError:
  19. return payload.decode("utf-8")
  20. def command_int_value(value):
  21. try:
  22. return int(value)
  23. except TypeError:
  24. print("You need to give a integer parameter not '%s'" % str(value))
  25. def command_float_value(value):
  26. try:
  27. return float(value)
  28. except TypeError:
  29. print("You need to give a integer parameter not '%s'" % str(value))
  30. def devicename(topic):
  31. return " - ".join(topic.split('/')[1:])
  32. def percent_bar(value):
  33. rv = ""
  34. for i in range(0, 10):
  35. rv += u"\u25ac" if (value - 5) > 10*i else u"\u25ad"
  36. return rv
  37. def print_light(color, state, topic, description, led=False):
  38. if led is True:
  39. if state is True:
  40. icon = colored.fg('green') + "\u2b24" + color
  41. else:
  42. icon = colored.fg('light_gray') + "\u2b24" + color
  43. else:
  44. icon = u'\u2b24' if state is True else u'\u25ef'
  45. print(color + 10 * ' ' + icon + 9 * ' ' + devicename(topic), description + colored.attr("reset"))
  46. def print_switch(color, state, topic, description):
  47. icon = u'\u25a0' if state is True else u'\u25a1'
  48. print(color + 10 * ' ' + icon + 9 * ' ' + devicename(topic), description + colored.attr("reset"))
  49. def print_percent(color, prefix, perc_value, value_str, topic, description):
  50. if len(prefix) > 1 or len(value_str) > 7:
  51. raise ValueError("Length of prefix (%d) > 1 or length of value_str (%d) > 7" % (len(prefix), len(value_str)))
  52. print(color + prefix + percent_bar(perc_value), value_str + (8 - len(value_str)) * ' ' + devicename(topic), description + colored.attr("reset"))
  53. class base(object):
  54. AUTOSEND = True
  55. COMMANDS = []
  56. def __init__(self, mqtt_client, topic):
  57. self.mqtt_client = mqtt_client
  58. self.topic = topic
  59. #
  60. self.data = {}
  61. self.callbacks = {}
  62. self.names = {}
  63. self.commands = self.COMMANDS[:]
  64. #
  65. self.mqtt_client.add_callback(self.topic, self.__rx__)
  66. self.mqtt_client.add_callback(self.topic + '/#', self.__rx__)
  67. def add_callback(self, key, callback, value):
  68. if self.callbacks.get(key) is None:
  69. self.callbacks[key] = []
  70. self.callbacks[key].append((callback, value))
  71. def add_channel_name(self, key, name):
  72. self.names[key] = name
  73. def capabilities(self):
  74. return self.commands
  75. def store_data(self, *args, **kwargs):
  76. keys_changed = []
  77. for key in kwargs:
  78. if kwargs[key] is not None and kwargs[key] != self.data.get(key):
  79. keys_changed.append(key)
  80. self.data[key] = kwargs[key]
  81. for callback, value in self.callbacks.get(key, [(None, None)]):
  82. if callback is not None and (value is None or value == kwargs[key]):
  83. callback(self, key, kwargs[key])
  84. if self.AUTOSEND and len(keys_changed) > 0:
  85. self.__tx__(keys_changed)
  86. def get_data(self, key, default=None):
  87. rv = self.data.get(key, default)
  88. try:
  89. rv = True if rv.lower() == 'on' else rv
  90. rv = False if rv.lower() == 'off' else rv
  91. except AttributeError:
  92. pass
  93. return rv
  94. def __tx__(self, keys_changed):
  95. self.mqtt_client.send(self.topic, json.dumps(self.data))
  96. def __rx__(self, client, userdata, message):
  97. print("%s: __rx__ not handled!" % self.__class__.__name__)
  98. class shelly(base):
  99. KEY_OUTPUT_0 = "relay/0"
  100. KEY_OUTPUT_1 = "relay/1"
  101. KEY_INPUT_0 = "input/0"
  102. KEY_INPUT_1 = "input/1"
  103. KEY_LONGPUSH_0 = "longpush/0"
  104. KEY_LONGPUSH_1 = "longpush/1"
  105. #
  106. INPUT_FUNC_OUT1_FOLLOW = "out1_follow"
  107. INPUT_FUNC_OUT1_TRIGGER = "out1_trigger"
  108. INPUT_FUNC_OUT2_FOLLOW = "out2_follow"
  109. INPUT_FUNC_OUT2_TRIGGER = "out2_trigger"
  110. #
  111. COMMANDS = [
  112. "get_relay_0", "toggle_relay_0",
  113. "get_relay_1", "toggle_relay_1",
  114. "get_input_0", "toggle_input_0",
  115. "get_input_1", "toggle_input_1",
  116. "trigger_long_input_0", "trigger_long_input_1",
  117. ]
  118. def __init__(self, mqtt_client, topic, input_0_func=None, input_1_func=None, output_0_auto_off=None):
  119. super().__init__(mqtt_client, topic)
  120. #
  121. self.store_data(**{self.KEY_OUTPUT_0: False, self.KEY_OUTPUT_1: False, self.KEY_INPUT_0: False, self.KEY_INPUT_1: False})
  122. self.__input_0_func = input_0_func
  123. self.__input_1_func = input_1_func
  124. self.__output_0_auto_off__ = output_0_auto_off
  125. if self.__output_0_auto_off__ is not None:
  126. self.__delayed_off__ = task.delayed(float(self.__output_0_auto_off__), self.__auto_off__, self.KEY_OUTPUT_0)
  127. #
  128. self.add_callback(self.KEY_OUTPUT_0, self.print_formatted, None)
  129. self.add_callback(self.KEY_OUTPUT_0, self.__start_auto_off__, True)
  130. self.add_callback(self.KEY_OUTPUT_0, self.__stop_auto_off__, True)
  131. self.add_callback(self.KEY_OUTPUT_1, self.print_formatted, None)
  132. #
  133. self.add_callback(self.KEY_INPUT_0, self.__input_function__, None)
  134. self.add_callback(self.KEY_INPUT_1, self.__input_function__, None)
  135. def __rx__(self, client, userdata, message):
  136. value = payload_filter(message.payload)
  137. if message.topic.startswith(self.topic) and message.topic.endswith("/command"):
  138. key = '/'.join(message.topic.split('/')[-3:-1])
  139. if value == 'toggle':
  140. self.__toggle_data__(key)
  141. else:
  142. self.__set_data__(key, value)
  143. def __tx__(self, keys_changed):
  144. for key in keys_changed:
  145. self.mqtt_client.send(self.topic + '/' + key, "on" if self.data.get(key) else "off")
  146. def __input_function__(self, device, key, data):
  147. if key == self.KEY_INPUT_0:
  148. func = self.__input_0_func
  149. elif key == self.KEY_INPUT_1:
  150. func = self.__input_1_func
  151. else:
  152. func = None
  153. if func == self.INPUT_FUNC_OUT1_FOLLOW:
  154. self.__set_data__(self.KEY_OUTPUT_0, data)
  155. elif func == self.INPUT_FUNC_OUT1_TRIGGER:
  156. self.__toggle_data__(self.KEY_OUTPUT_0)
  157. elif func == self.INPUT_FUNC_OUT2_FOLLOW:
  158. self.__set_data__(self.KEY_OUTPUT_1, data)
  159. elif func == self.INPUT_FUNC_OUT2_TRIGGER:
  160. self.__toggle_data__(self.KEY_OUTPUT_1)
  161. def __start_auto_off__(self, device, key, data):
  162. self.__stop_auto_off__(device, key, data)
  163. if self.__output_0_auto_off__ is not None:
  164. self.__delayed_off__.run()
  165. def __stop_auto_off__(self, device, key, data):
  166. if self.__output_0_auto_off__ is not None:
  167. if not self.__delayed_off__._stopped:
  168. self.__delayed_off__.stop()
  169. def __auto_off__(self, key):
  170. if key == self.KEY_OUTPUT_0:
  171. self.__set_data__(key, 'off')
  172. def __set_data__(self, key, value):
  173. self.store_data(**{key: value == "on"})
  174. def __toggle_data__(self, key):
  175. if key in self.data:
  176. self.__set_data__(key, "on" if not self.data.get(key) else "off")
  177. def command(self, command):
  178. if command in self.COMMANDS:
  179. if command == self.COMMANDS[0]:
  180. self.print_formatted(self, self.KEY_OUTPUT_0, self.data.get(self.KEY_OUTPUT_0))
  181. elif command == self.COMMANDS[1]:
  182. self.__toggle_data__(self.KEY_OUTPUT_0)
  183. elif command == self.COMMANDS[2]:
  184. self.print_formatted(self, self.KEY_OUTPUT_1, self.data.get(self.KEY_OUTPUT_1))
  185. elif command == self.COMMANDS[3]:
  186. self.__toggle_data__(self.KEY_OUTPUT_1)
  187. elif command == self.COMMANDS[4]:
  188. self.print_formatted(self, self.KEY_INPUT_0, self.data.get(self.KEY_INPUT_0))
  189. elif command == self.COMMANDS[5]:
  190. self.__toggle_data__(self.KEY_INPUT_0)
  191. elif command == self.COMMANDS[6]:
  192. self.print_formatted(self, self.KEY_INPUT_1, self.data.get(self.KEY_INPUT_1))
  193. elif command == self.COMMANDS[7]:
  194. self.__toggle_data__(self.KEY_INPUT_1)
  195. elif command == self.COMMANDS[8]:
  196. self.__toggle_data__(self.KEY_INPUT_0)
  197. time.sleep(0.4)
  198. self.__set_data__(self.KEY_LONGPUSH_0, True)
  199. time.sleep(0.1)
  200. self.__set_data__(self.KEY_LONGPUSH_0, False)
  201. elif command == self.COMMANDS[9]:
  202. self.__toggle_data__(self.KEY_INPUT_1)
  203. time.sleep(0.4)
  204. self.__set_data__(self.KEY_LONGPUSH_1, True)
  205. time.sleep(0.1)
  206. self.__set_data__(self.KEY_LONGPUSH_1, False)
  207. else:
  208. print("%s: not yet implemented!" % command)
  209. else:
  210. print("Unknown command!")
  211. def print_formatted(self, device, key, value):
  212. if value is not None:
  213. info = (" - %ds" % self.__output_0_auto_off__) if self.__output_0_auto_off__ is not None and value else ""
  214. channel = "(%s%s)" % (self.names.get(key, key), info)
  215. print_light(COLOR_SHELLY, value, self.topic, channel)
  216. class my_powerplug(base):
  217. KEY_OUTPUT_0 = "state"
  218. COMMANDS = [
  219. "get_output", "toggle_output",
  220. ]
  221. def __init__(self, mqtt_client, topic, channel):
  222. super().__init__(mqtt_client, topic + '/' + "output/%d" % (channel + 1))
  223. #
  224. self.data[self.KEY_OUTPUT_0] = False
  225. self.add_callback(self.KEY_OUTPUT_0, self.print_formatted, None)
  226. def __rx__(self, client, userdata, message):
  227. if message.topic == self.topic + '/set':
  228. payload = payload_filter(message.payload)
  229. if payload == "toggle":
  230. payload = not self.data.get(self.KEY_OUTPUT_0)
  231. self.store_data(**{self.KEY_OUTPUT_0: payload})
  232. def __tx__(self, keys_changed):
  233. for key in keys_changed:
  234. self.mqtt_client.send(self.topic, json.dumps(self.data.get(key)))
  235. def command(self, command):
  236. if command in self.COMMANDS:
  237. if command == self.COMMANDS[0]:
  238. self.print_formatted(self, self.KEY_OUTPUT_0, self.data.get(self.KEY_OUTPUT_0))
  239. elif command == self.COMMANDS[1]:
  240. self.store_data(**{self.KEY_OUTPUT_0: not self.data.get(self.KEY_OUTPUT_0)})
  241. else:
  242. print("%s: not yet implemented!" % command)
  243. else:
  244. print("Unknown command!")
  245. def print_formatted(self, device, key, value):
  246. if value is not None:
  247. print_light(COLOR_POWERPLUG, value, self.topic, "(%s)" % self.names.get(key, "State"))
  248. class silvercrest_powerplug(base):
  249. KEY_OUTPUT_0 = "state"
  250. #
  251. COMMANDS = [
  252. "get_state", "set_state", "unset_state",
  253. ]
  254. def __init__(self, mqtt_client, topic):
  255. super().__init__(mqtt_client, topic)
  256. self.add_callback(self.KEY_OUTPUT_0, self.print_formatted, None)
  257. #
  258. self.store_data(**{self.KEY_OUTPUT_0: False})
  259. def __rx__(self, client, userdata, message):
  260. if message.topic == self.topic + '/set':
  261. STATES = ["on", "off", "toggle"]
  262. #
  263. state = json.loads(message.payload).get('state').lower()
  264. if state in STATES:
  265. if state == STATES[0]:
  266. self.store_data(**{self.KEY_OUTPUT_0: True})
  267. elif state == STATES[1]:
  268. self.store_data(**{self.KEY_OUTPUT_0: False})
  269. else:
  270. self.store_data(**{not self.data.get(self.KEY_OUTPUT_0)})
  271. def __tx__(self, keys_changed):
  272. for key in keys_changed:
  273. self.mqtt_client.send(self.topic + '/' + key, "on" if self.data.get(key) else "off")
  274. def command(self, command):
  275. if command in self.COMMANDS:
  276. if command == self.COMMANDS[0]:
  277. self.print_formatted(self, self.KEY_OUTPUT_0, self.data.get(self.KEY_OUTPUT_0))
  278. elif command == self.COMMANDS[1]:
  279. self.store_data(**{self.KEY_OUTPUT_0: True})
  280. elif command == self.COMMANDS[2]:
  281. self.store_data(**{self.KEY_OUTPUT_0: False})
  282. else:
  283. print("%s: not yet implemented!" % command)
  284. else:
  285. print("Unknown command!")
  286. def print_formatted(self, device, key, value):
  287. if value is not None:
  288. print_light(COLOR_POWERPLUG, value, self.topic, "(%s)" % self.names.get(key, key))
  289. class tradfri_light(base):
  290. KEY_OUTPUT_0 = "state"
  291. KEY_BRIGHTNESS = "brightness"
  292. KEY_COLOR_TEMP = "color_temp"
  293. KEY_BRIGHTNESS_MOVE = "brightness_move"
  294. #
  295. STATE_COMMANDS = ("get_state", "toggle_state", )
  296. BRIGHTNESS_COMMANDS = ("get_brightness", "set_brightness",)
  297. COLOR_TEMP_COMMANDS = ("get_color_temp", "set_color_temp",)
  298. def __init__(self, mqtt_client, topic, enable_state=True, enable_brightness=False, enable_color_temp=False, send_on_power_on=True):
  299. super().__init__(mqtt_client, topic)
  300. self.send_on_power_on = send_on_power_on
  301. self.add_callback(self.KEY_OUTPUT_0, self.print_formatted, None)
  302. self.add_callback(self.KEY_BRIGHTNESS, self.print_formatted, None)
  303. self.add_callback(self.KEY_COLOR_TEMP, self.print_formatted, None)
  304. #
  305. self.commands = []
  306. if enable_state:
  307. self.commands.extend(self.STATE_COMMANDS)
  308. if enable_brightness:
  309. self.commands.extend(self.BRIGHTNESS_COMMANDS)
  310. if enable_color_temp:
  311. self.commands.extend(self.COLOR_TEMP_COMMANDS)
  312. self.__init_data__(enable_state, enable_brightness, enable_color_temp)
  313. def __init_data__(self, enable_state, enable_brightness, enable_color_temp):
  314. data = {}
  315. if enable_state:
  316. data[self.KEY_OUTPUT_0] = False
  317. self.commands.extend(self.STATE_COMMANDS)
  318. if enable_brightness:
  319. data[self.KEY_BRIGHTNESS] = 50
  320. self.brightnes_move = (0, time.time())
  321. self.commands.extend(self.BRIGHTNESS_COMMANDS)
  322. if enable_color_temp:
  323. data[self.KEY_COLOR_TEMP] = 5
  324. self.commands.extend(self.COLOR_TEMP_COMMANDS)
  325. self.store_data(**data)
  326. def __rx__(self, client, userdata, message):
  327. data = json.loads(message.payload)
  328. if self.data.get(self.KEY_OUTPUT_0) or data.get(self.KEY_OUTPUT_0) in ['on', 'toggle']:
  329. if message.topic.startswith(self.topic) and message.topic.endswith('/set'):
  330. for targetkey in data:
  331. value = data[targetkey]
  332. if targetkey in self.data.keys():
  333. if targetkey == self.KEY_OUTPUT_0:
  334. if value == "toggle":
  335. value = not self.data.get(self.KEY_OUTPUT_0)
  336. else:
  337. value = value == "on"
  338. elif targetkey == self.KEY_BRIGHTNESS:
  339. value = round((value - 1) / 2.53, 0)
  340. elif targetkey == self.KEY_COLOR_TEMP:
  341. value = round((value - 250) / 20.4, 0)
  342. self.store_data(**{targetkey: value})
  343. else:
  344. if targetkey == self.KEY_BRIGHTNESS_MOVE:
  345. new_value = self.data.get(self.KEY_BRIGHTNESS) + (time.time() - self.brightnes_move[1]) * self.brightnes_move[0]
  346. if new_value < 0:
  347. new_value = 0
  348. if new_value > 256:
  349. new_value = 256
  350. self.store_data(**{self.KEY_BRIGHTNESS: int(new_value)})
  351. self.brightnes_move = (value, time.time())
  352. else:
  353. print("%s: UNKNOWN KEY %s" % (message.topic, targetkey))
  354. elif message.topic == self.topic + '/get':
  355. self.__tx__(None)
  356. def __tx__(self, keys_changed):
  357. tx_data = copy.copy(self.data)
  358. if self.KEY_OUTPUT_0 in tx_data:
  359. tx_data[self.KEY_OUTPUT_0] = "on" if tx_data[self.KEY_OUTPUT_0] else "off"
  360. if self.KEY_BRIGHTNESS in tx_data:
  361. tx_data[self.KEY_BRIGHTNESS] = 1 + round(2.53 * tx_data[self.KEY_BRIGHTNESS], 0)
  362. if self.KEY_COLOR_TEMP in tx_data:
  363. tx_data[self.KEY_COLOR_TEMP] = 250 + round(20.4 * tx_data[self.KEY_COLOR_TEMP], 0)
  364. self.mqtt_client.send(self.topic, json.dumps(tx_data))
  365. def command(self, command):
  366. try:
  367. command, value = command.split(' ')
  368. except ValueError:
  369. value = None
  370. if command in self.capabilities():
  371. if command == self.STATE_COMMANDS[0]:
  372. self.print_formatted(self, self.KEY_OUTPUT_0, self.data.get(self.KEY_OUTPUT_0))
  373. elif command == self.STATE_COMMANDS[1]:
  374. self.store_data(**{self.KEY_OUTPUT_0: not self.data.get(self.KEY_OUTPUT_0)})
  375. elif command == self.BRIGHTNESS_COMMANDS[0]:
  376. self.print_formatted(self, self.KEY_BRIGHTNESS, self.data.get(self.KEY_BRIGHTNESS))
  377. elif command == self.BRIGHTNESS_COMMANDS[1]:
  378. self.store_data(**{self.KEY_BRIGHTNESS: command_int_value(value)})
  379. elif command == self.COLOR_TEMP_COMMANDS[0]:
  380. self.print_formatted(self, self.KEY_COLOR_TEMP, self.data.get(self.KEY_COLOR_TEMP))
  381. elif command == self.COLOR_TEMP_COMMANDS[1]:
  382. self.store_data(**{self.KEY_COLOR_TEMP: command_int_value(value)})
  383. else:
  384. print("%s: not yet implemented!" % command)
  385. else:
  386. print("Unknown command!")
  387. def power_off(self, device, key, value):
  388. self.data[self.KEY_OUTPUT_0] = False
  389. self.print_formatted(self, self.KEY_OUTPUT_0, False)
  390. def power_on(self, device, key, value):
  391. if self.send_on_power_on:
  392. self.store_data(**{self.KEY_OUTPUT_0: True})
  393. else:
  394. self.data[self.KEY_OUTPUT_0] = True
  395. self.print_formatted(self, self.KEY_OUTPUT_0, True)
  396. def print_formatted(self, device, key, value):
  397. if value is not None:
  398. color = COLOR_LIGHT_ACTIVE
  399. if key == self.KEY_OUTPUT_0:
  400. print_light(COLOR_LIGHT_ACTIVE, value, self.topic, "")
  401. self.print_formatted(device, self.KEY_BRIGHTNESS, self.data.get(self.KEY_BRIGHTNESS))
  402. self.print_formatted(device, self.KEY_COLOR_TEMP, self.data.get(self.KEY_COLOR_TEMP))
  403. elif key in [self.KEY_BRIGHTNESS, self.KEY_COLOR_TEMP]:
  404. perc_value = round(value, 0) if key == self.KEY_BRIGHTNESS else round(10 * value, 0)
  405. print_percent(
  406. COLOR_LIGHT_PASSIVE if not self.data.get(self.KEY_OUTPUT_0) else COLOR_LIGHT_ACTIVE,
  407. 'B' if key == self.KEY_BRIGHTNESS else 'C',
  408. perc_value,
  409. "%3d%%" % perc_value,
  410. self.topic,
  411. ""
  412. )
  413. class brennenstuhl_heating_valve(base):
  414. TEMP_RANGE = [10, 30]
  415. #
  416. KEY_TEMPERATURE_SETPOINT = "current_heating_setpoint"
  417. KEY_TEMPERATURE = "local_temperature"
  418. #
  419. COMMANDS = [
  420. "get_temperature_setpoint", "set_temperature_setpoint", "set_local_temperature",
  421. ]
  422. def __init__(self, mqtt_client, topic):
  423. super().__init__(mqtt_client, topic)
  424. self.store_data(**{
  425. self.KEY_TEMPERATURE_SETPOINT: 20,
  426. self.KEY_TEMPERATURE: 20.7,
  427. })
  428. self.add_callback(self.KEY_TEMPERATURE_SETPOINT, self.print_formatted, None)
  429. def __rx__(self, client, userdata, message):
  430. if message.topic.startswith(self.topic) and message.topic.endswith("/set"):
  431. payload = payload_filter(message.payload)
  432. self.store_data(**payload)
  433. def command(self, command):
  434. try:
  435. command, value = command.split(' ')
  436. except ValueError:
  437. value = None
  438. if command in self.COMMANDS:
  439. if command == self.COMMANDS[0]:
  440. self.print_formatted(self, self.KEY_TEMPERATURE_SETPOINT, self.data.get(self.KEY_TEMPERATURE_SETPOINT))
  441. elif command == self.COMMANDS[1]:
  442. self.store_data(**{self.KEY_TEMPERATURE_SETPOINT: command_float_value(value)})
  443. elif command == self.COMMANDS[2]:
  444. self.store_data(**{self.KEY_TEMPERATURE: command_float_value(value)})
  445. def print_formatted(self, device, key, value):
  446. devicename = ' - '.join(self.topic.split('/')[1:])
  447. if key == self.KEY_TEMPERATURE_SETPOINT:
  448. perc = 100 * (value - self.TEMP_RANGE[0]) / (self.TEMP_RANGE[1] - self.TEMP_RANGE[0])
  449. perc = 100 if perc > 100 else perc
  450. perc = 0 if perc < 0 else perc
  451. print_percent(COLOR_HEATING_VALVE, '\u03d1', perc, "%4.1f°C" % value, self.topic, "")
  452. class videv_light(base):
  453. AUTOSEND = False
  454. #
  455. KEY_STATE = "state"
  456. KEY_BRIGHTNESS = "brightness"
  457. KEY_COLOR_TEMP = "color_temp"
  458. KEY_TIMER = "timer"
  459. #
  460. STATE_COMMANDS = ("get_state", "toggle_state", )
  461. BRIGHTNESS_COMMANDS = ("get_brightness", "set_brightness", )
  462. COLOR_TEMP_COMMANDS = ("get_color_temp", "set_color_temp", )
  463. TIMER_COMMANDS = ("get_timer", )
  464. def __init__(self, mqtt_client, topic, enable_state=True, enable_brightness=False, enable_color_temp=False, enable_timer=False):
  465. super().__init__(mqtt_client, topic)
  466. self.enable_state = enable_state
  467. self.enable_brightness = enable_brightness
  468. self.enable_color_temp = enable_color_temp
  469. self.enable_timer = enable_timer
  470. #
  471. self.maxvalue = None
  472. # add commands to be available
  473. if enable_state:
  474. # init default value
  475. self.data[self.KEY_STATE] = False
  476. # add print callback
  477. self.add_callback(self.KEY_STATE, self.print_formatted, None)
  478. # add commands to be available
  479. self.commands.extend(self.STATE_COMMANDS)
  480. if enable_brightness:
  481. # init default value
  482. self.data[self.KEY_BRIGHTNESS] = 50
  483. # add print callback
  484. self.add_callback(self.KEY_BRIGHTNESS, self.print_formatted, None)
  485. # add commands to be available
  486. self.commands.extend(self.BRIGHTNESS_COMMANDS)
  487. if enable_color_temp:
  488. # init default value
  489. self.data[self.KEY_COLOR_TEMP] = 5
  490. # add print callback
  491. self.add_callback(self.KEY_COLOR_TEMP, self.print_formatted, None)
  492. # add commands to be available
  493. self.commands.extend(self.COLOR_TEMP_COMMANDS)
  494. if enable_timer:
  495. # init default value
  496. self.data[self.KEY_TIMER] = 0
  497. # add print callback
  498. self.add_callback(self.KEY_TIMER, self.print_formatted, None)
  499. # add commands to be available
  500. self.commands.extend(self.TIMER_COMMANDS)
  501. def __rx__(self, client, userdata, message):
  502. value = payload_filter(message.payload)
  503. if message.topic.startswith(self.topic):
  504. targetkey = message.topic.split('/')[-1]
  505. if targetkey in self.data.keys():
  506. self.store_data(**{targetkey: value})
  507. elif targetkey != "__info__":
  508. print("Unknown key %s in %s::%s" % (targetkey, message.topic, self.__class__.__name__))
  509. elif message.topic == self.topic + '/get':
  510. self.__tx__(None)
  511. def send(self, key, data):
  512. if data is not None:
  513. topic = self.topic + '/' + key
  514. self.mqtt_client.send(topic, json.dumps(data))
  515. def command(self, command):
  516. try:
  517. command, value = command.split(' ')
  518. except ValueError:
  519. value = None
  520. if command in self.capabilities():
  521. if command == self.STATE_COMMANDS[0]:
  522. self.print_formatted(self, self.KEY_STATE, self.data.get(self.KEY_STATE))
  523. elif command == self.STATE_COMMANDS[1]:
  524. self.send(self.KEY_STATE, not self.data.get(self.KEY_STATE))
  525. elif command == self.BRIGHTNESS_COMMANDS[0]:
  526. self.print_formatted(self, self.KEY_BRIGHTNESS, self.data.get(self.KEY_BRIGHTNESS))
  527. elif command == self.BRIGHTNESS_COMMANDS[1]:
  528. self.send(self.KEY_BRIGHTNESS, command_int_value(value))
  529. elif command == self.COLOR_TEMP_COMMANDS[0]:
  530. self.print_formatted(self, self.KEY_COLOR_TEMP, self.data.get(self.KEY_COLOR_TEMP))
  531. elif command == self.COLOR_TEMP_COMMANDS[1]:
  532. self.send(self.KEY_COLOR_TEMP, command_int_value(value))
  533. elif command == self.TIMER_COMMANDS[0]:
  534. self.print_formatted(self, self.KEY_TIMER, self.data.get(self.KEY_TIMER))
  535. else:
  536. print("%s: not yet implemented!" % command)
  537. else:
  538. print("Unknown command!")
  539. def print_formatted(self, device, key, value):
  540. if value is not None:
  541. device = " - ".join(self.topic.split('/')[1:])
  542. if key == self.KEY_STATE:
  543. print_switch(COLOR_GUI_ACTIVE, value, self.topic, "")
  544. elif key in [self.KEY_BRIGHTNESS, self.KEY_COLOR_TEMP]:
  545. perc_value = round(value * 10 if key == self.KEY_COLOR_TEMP else value, 0)
  546. print_percent(
  547. COLOR_GUI_ACTIVE,
  548. 'B' if key == self.KEY_BRIGHTNESS else 'C',
  549. perc_value,
  550. "%3d%%" % perc_value,
  551. self.topic,
  552. ""
  553. )
  554. elif key == self.KEY_TIMER:
  555. if value > 0:
  556. if self.maxvalue is None and value != 0:
  557. self.maxvalue = value
  558. disp_value = value
  559. try:
  560. perc = disp_value / self.maxvalue * 100
  561. except ZeroDivisionError:
  562. perc = 0
  563. else:
  564. disp_value = 0
  565. perc = 0
  566. self.maxvalue = None
  567. print_percent(COLOR_GUI_ACTIVE, 't', perc, '%3d%%' % perc, self.topic, '(%.1f)' % disp_value)
  568. # class silvercrest_motion_sensor(base):
  569. # KEY_OCCUPANCY = "occupancy"
  570. # COMMANDS = ['motion']
  571. # def __init__(self, mqtt_client, topic):
  572. # super().__init__(mqtt_client, topic)
  573. # self.data[self.KEY_OCCUPANCY] = False
  574. # self.add_callback(self.KEY_OCCUPANCY, self.print_formatted, None)
  575. # def __rx__(self, client, userdata, message):
  576. # pass
  577. # def command(self, command):
  578. # try:
  579. # command, value = command.split(' ')
  580. # except ValueError:
  581. # value = None
  582. # else:
  583. # value = json.loads(value)
  584. # if command == self.COMMANDS[0]:
  585. # self.store_data(**{self.KEY_OCCUPANCY: True})
  586. # time.sleep(value or 10)
  587. # self.store_data(**{self.KEY_OCCUPANCY: False})
  588. # def print_formatted(self, device, key, value):
  589. # if value is not None:
  590. # print_light(COLOR_MOTION_SENSOR, value, self.topic, "")
  591. # class tradfri_button(base):
  592. # KEY_ACTION = "action"
  593. # #
  594. # ACTION_TOGGLE = "toggle"
  595. # ACTION_BRIGHTNESS_UP = "brightness_up_click"
  596. # ACTION_BRIGHTNESS_DOWN = "brightness_down_click"
  597. # ACTION_RIGHT = "arrow_right_click"
  598. # ACTION_LEFT = "arrow_left_click"
  599. # ACTION_BRIGHTNESS_UP_LONG = "brightness_up_hold"
  600. # ACTION_BRIGHTNESS_DOWN_LONG = "brightness_down_hold"
  601. # ACTION_RIGHT_LONG = "arrow_right_hold"
  602. # ACTION_LEFT_LONG = "arrow_left_hold"
  603. # #
  604. # COMMANDS = [ACTION_TOGGLE, ACTION_LEFT, ACTION_RIGHT, ACTION_BRIGHTNESS_UP, ACTION_BRIGHTNESS_DOWN,
  605. # ACTION_LEFT_LONG, ACTION_RIGHT_LONG, ACTION_BRIGHTNESS_UP_LONG, ACTION_BRIGHTNESS_DOWN_LONG]
  606. # def __init__(self, mqtt_client, topic):
  607. # super().__init__(mqtt_client, topic)
  608. # def __rx__(self, client, userdata, message):
  609. # pass
  610. # def command(self, command):
  611. # try:
  612. # command, value = command.split(' ')
  613. # except ValueError:
  614. # value = None
  615. # else:
  616. # value = json.loads(value)
  617. # if command in self.capabilities():
  618. # action = self.COMMANDS[self.COMMANDS.index(command)]
  619. # if self.COMMANDS.index(command) <= 4:
  620. # self.mqtt_client.send(self.topic, json.dumps({self.KEY_ACTION: action}))
  621. # elif self.COMMANDS.index(command) <= 8:
  622. # self.mqtt_client.send(self.topic, json.dumps({self.KEY_ACTION: action}))
  623. # time.sleep(value or 0.5)
  624. # action = '_'.join(action.split('_')[:-1] + ['release'])
  625. # self.mqtt_client.send(self.topic, json.dumps({self.KEY_ACTION: action}))
  626. # class remote(base):
  627. # def __rx__(self, client, userdata, message):
  628. # if message.topic == self.topic + "/VOLUP":
  629. # if payload_filter(message.payload):
  630. # icon = u'\u1403'
  631. # else:
  632. # icon = u'\u25a1'
  633. # elif message.topic == self.topic + "/VOLDOWN":
  634. # if payload_filter(message.payload):
  635. # icon = u'\u1401'
  636. # else:
  637. # icon = u'\u25a1'
  638. # else:
  639. # return
  640. # devicename = ' - '.join(self.topic.split('/')[1:-1])
  641. # print(COLOR_REMOTE + 10 * ' ' + icon + 6 * ' ' + devicename + colored.attr("reset"))
  642. # class gui_heating_valve(base):
  643. # AUTOSEND = False
  644. # #
  645. # TEMP_RANGE = [10, 30]
  646. # #
  647. # KEY_TIMER = "timer"
  648. # KEY_TEMPERATURE = "temperature"
  649. # KEY_SETPOINT_TEMP = "setpoint_temp"
  650. # KEY_SETPOINT_TO_DEFAULT = "setpoint_to_default"
  651. # KEY_BOOST = 'boost'
  652. # KEY_AWAY = "away"
  653. # KEY_SUMMER = "summer"
  654. # KEY_ENABLE = "enable"
  655. # #
  656. # COMMANDS = [
  657. # "get_temperature",
  658. # "get_temperature_setpoint", "set_temperature_setpoint",
  659. # "trigger_boost", "trigger_setpoint_to_default",
  660. # "toggle_away", "toggle_summer",
  661. # ]
  662. # def __init__(self, mqtt_client, topic):
  663. # super().__init__(mqtt_client, topic)
  664. # self.add_callback(self.KEY_SETPOINT_TEMP, self.print_formatted, None)
  665. # self.add_callback(self.KEY_TIMER, self.print_formatted, None)
  666. # self.add_callback(self.KEY_AWAY, self.print_formatted, None)
  667. # self.add_callback(self.KEY_SUMMER, self.print_formatted, None)
  668. # #
  669. # self.store_data(**{
  670. # self.KEY_TEMPERATURE: 20.7,
  671. # self.KEY_SETPOINT_TEMP: 20,
  672. # self.KEY_TIMER: 0,
  673. # self.KEY_AWAY: False,
  674. # self.KEY_SUMMER: False,
  675. # self.KEY_ENABLE: True
  676. # })
  677. # def __rx__(self, client, userdata, message):
  678. # value = payload_filter(message.payload)
  679. # if message.topic.startswith(self.topic) and message.topic.endswith('/set'):
  680. # targetkey = message.topic.split('/')[-2]
  681. # if targetkey in self.data.keys():
  682. # self.store_data(**{targetkey: value})
  683. # else:
  684. # print("Unknown key %s in %s::%s" % (targetkey, message.topic, self.__class__.__name__))
  685. # elif message.topic == self.topic + '/get':
  686. # self.__tx__(None)
  687. # def send(self, key, data):
  688. # if data is not None:
  689. # topic = self.topic + '/' + key
  690. # self.mqtt_client.send(topic, json.dumps(data))
  691. # def command(self, command):
  692. # try:
  693. # command, value = command.split(' ')
  694. # except ValueError:
  695. # value = None
  696. # if command in self.COMMANDS:
  697. # if command == self.COMMANDS[0]:
  698. # self.print_formatted(self, self.KEY_TEMPERATURE, self.data.get(self.KEY_TEMPERATURE))
  699. # elif command == self.COMMANDS[1]:
  700. # self.print_formatted(self, self.KEY_SETPOINT_TEMP, self.data.get(self.KEY_SETPOINT_TEMP))
  701. # elif command == self.COMMANDS[2]:
  702. # self.send(self.KEY_SETPOINT_TEMP, command_float_value(value))
  703. # elif command == self.COMMANDS[3]:
  704. # self.send(self.KEY_BOOST, True)
  705. # elif command == self.COMMANDS[4]:
  706. # self.send(self.KEY_SETPOINT_TO_DEFAULT, True)
  707. # elif command == self.COMMANDS[5]:
  708. # self.send(self.KEY_AWAY, not self.data.get(self.KEY_AWAY))
  709. # elif command == self.COMMANDS[6]:
  710. # self.send(self.KEY_SUMMER, not self.data.get(self.KEY_SUMMER))
  711. # def print_formatted(self, device, key, value):
  712. # devicename = ' - '.join(self.topic.split('/')[1:])
  713. # if key == self.KEY_TIMER:
  714. # value /= 60
  715. # try:
  716. # perc = 100 * value / 60
  717. # except TypeError:
  718. # value = 0
  719. # perc = 0
  720. # print_percent(COLOR_GUI_ACTIVE, 't', perc, "%4.1fmin" % value, self.topic, "(Timer)")
  721. # elif key == self.KEY_TEMPERATURE:
  722. # perc = 100 * (value - self.TEMP_RANGE[0]) / (self.TEMP_RANGE[1] - self.TEMP_RANGE[0])
  723. # perc = 100 if perc > 100 else perc
  724. # perc = 0 if perc < 0 else perc
  725. # print_percent(COLOR_GUI_ACTIVE, '\u03d1', perc, "%4.1f°C" % value, self.topic, "(Temperature)")
  726. # elif key == self.KEY_SETPOINT_TEMP:
  727. # perc = 100 * (value - self.TEMP_RANGE[0]) / (self.TEMP_RANGE[1] - self.TEMP_RANGE[0])
  728. # perc = 100 if perc > 100 else perc
  729. # perc = 0 if perc < 0 else perc
  730. # print_percent(COLOR_GUI_ACTIVE if self.data.get(self.KEY_ENABLE) else COLOR_GUI_PASSIVE,
  731. # '\u03d1', perc, "%4.1f°C" % value, self.topic, "(Setpoint)")
  732. # elif key == self.KEY_AWAY:
  733. # print_switch(COLOR_GUI_ACTIVE, value, self.topic, "(Away Mode)")
  734. # elif key == self.KEY_SUMMER:
  735. # print_switch(COLOR_GUI_ACTIVE, value, self.topic, "(Summer Mode)")