import appdaemon.plugins.hass.hassapi as hass import struct import binascii class Climate(hass.Hass): ir_topic = "home/remote/rm2/code/set" split_state = "None" timer = None def initialize(self): self.listen_event_handle_list = [] if "ha_panel" not in self.args or "door_window" not in self.args: self.error("Please provide temp_sensor, ha_panel, door_window in config!") return self.listen_event_handle_list.append(self.listen_state(self.constraint_changed, self.args['constraint'])) self.listen_event_handle_list.append(self.listen_state(self.ha_panel_changed, self.args['ha_panel'])) self.listen_event_handle_list.append(self.listen_state(self.door_window_changed, self.args['door_window'])) self.listen_event_handle_list.append(self.listen_state(self.nest_changed, 'binary_sensor.nest_y1')) self.listen_event_handle_list.append(self.listen_state(self.nest_changed, 'binary_sensor.nest_w1')) self.do_action() def nest_changed(self, entity, attribute, old, new, kwargs): self.do_action() def door_window_changed(self, entity, attribute, old, new, kwargs): self.do_action() def ha_panel_changed(self, entity, attribute, old, new, kwargs): self.do_action() def constraint_changed(self, entity, attribute, old, new, kwargs): self.do_action() def split_off(self): if self.split_state != "on" and self.split_state != "None": return remote = Remote() code = remote.set_mode("NONE", "NONE", 18, "OFF") self.call_service("mqtt/publish", topic = self.ir_topic, payload = code.decode("utf-8")) self.split_state = "off" if (self.timer != None): self.cancel_timer(self.timer) self.timer = self.run_in(self.run_in_split_off, 15) def run_in_split_off(self, args): self.turn_off("switch.plug_158d00010dd98d") def split_on(self, mode, temp): self.turn_on("switch.plug_158d00010dd98d") if (self.timer != None): self.cancel_timer(self.timer) self.timer = self.run_in(self.run_in_split_mode, 10, mode=mode, temp=temp, state="ON") def run_in_split_mode(self, args): remote = Remote() code = remote.set_mode(args['mode'], "2", args['temp'], args['state']) self.call_service("mqtt/publish", topic = self.ir_topic, payload = code.decode("utf-8")) def set_color_entity(self, to): if 'color_entity' not in self.args: return entity = self.args['color_entity'] if to == "hot": self.turn_on(entity, rgb_color=[255,0,0], brightness = 100) if to == "cold": self.turn_on(entity, rgb_color=[0,0,255], brightness = 100) if to == "off": self.turn_off(entity) def do_action(self): if 'constraint' in self.args and not self.constrain_input_boolean(self.args['constraint']): self.log("Temperature control is disabled.") self.split_off() return # Проверить alarm_panel ha_panel_state = self.get_state(self.args['ha_panel']) if (ha_panel_state != 'disarmed'): self.log("Nobody home. Turning off split.") self.split_off() return # Проверить датчики дверей и окон door_window_state = self.get_state(self.args['door_window']) if (door_window_state != 'off'): self.log("Balcony door is opened. Turning off split.") self.split_off() return nest_target_cool = self.get_state('climate.living_room', attribute="temperature") nest_target_hot = nest_target_cool if nest_target_cool == None and nest_target_hot == None: nest_target_cool = self.get_state('climate.living_room', attribute="target_temp_high") nest_target_hot = self.get_state('climate.living_room', attribute="target_temp_low") nest_w1 = "off" nest_y1 = "off" nest_w1 = self.get_state('binary_sensor.nest_w1') nest_y1 = self.get_state('binary_sensor.nest_y1') if nest_y1 == "on": self.split_state = "on" self.log("Turn on split for COOLING to {}.".format(nest_target_cool)) self.split_on("COOLING", 20) self.set_color_entity('cold') elif nest_w1 == "on": self.split_state = "on" self.log("Turn on split for HEATING to {}".format(nest_target_hot)) self.split_on("HEATING", 28) self.set_color_entity('hot') else: self.log("Nest is reporting that temp is ok.") self.split_off() self.set_color_entity('off') def terminate(self): if self.listen_event_handle_list != None: for listen_event_handle in self.listen_event_handle_list: self.cancel_listen_event(listen_event_handle) # # App to control climate device (split system) # # Args: # # notify = notification platform to send notifications to # # None # # Release Notes # # Version 1.0: # Initial Version # ------------------------------------------------------------------------------------------------- """ Library that genrates LG air conditioner remote codes """ FIRST_BYTE = 136 # b10001000 STATE = { "ON": 0, "OFF": 24, # b11000 "CHANGE_MODE": 1, } MODE = { "HEATING": 4, # b100 "AUTO": 3, # b011 "DEHUIDIFICATION": 1, # b001 "COOLING": 0, "NONE": 0, } TEMPERATURE_OFFSET = 15 FAN = { "1": 1, "2": 0, "3": 2, "4": 4, # b100 "NONE": 5, # b101 } FIRST_HIGH = 8271 FIRST_LOW = 4298 ZERO_AND_ONE_HIGH = 439 ZERO_LOW = 647 ONE_LOW = 1709 BUFFER_SIZE = 59 def test_bit(num, offset): """ Test the num(int) if at the given offset bit is 1 """ mask = 1 << offset return num & mask def set_bit(num, offset): """ Set bit to at the given offset to 1 """ mask = 1 << offset return num | mask class Remote(object): """ Library that genrates LG air conditioner remote codes """ def __init__(self): self.codes = [0] * BUFFER_SIZE self.crc = 0 def set_mode(self, mode, fan, temperature, state): """ Generate code and put it in the buffer """ self.codes[0] = FIRST_HIGH self.codes[1] = FIRST_LOW self.crc = 0 self.fill_buffer(0, 8, FIRST_BYTE) self.fill_buffer(8, 5, STATE[state]) if state == 'OFF': self.fill_buffer(13, 3, MODE['NONE']) else: self.fill_buffer(13, 3, MODE[mode]) if state == 'OFF': self.fill_buffer(16, 4, 0) else: self.fill_buffer(16, 4, temperature - TEMPERATURE_OFFSET) self.fill_buffer(20, 1, 0) # jet if state == 'OFF': self.fill_buffer(21, 3, FAN['NONE']) else: self.fill_buffer(21, 3, FAN[fan]) self.fill_buffer(24, 4, self.crc) self.codes[BUFFER_SIZE - 1] = ZERO_AND_ONE_HIGH self.codes = binascii.hexlify(self.lirc2broadlink(self.codes)) return self.codes def lirc2broadlink(self, pulses): array = bytearray() for pulse in pulses: pulse = int(pulse * 269 / 8192) # 32.84ms units if pulse < 256: array += bytearray(struct.pack('>B', pulse)) # big endian (1-byte) else: array += bytearray([0x00]) # indicate next number is 2-bytes array += bytearray(struct.pack('>H', pulse)) # big endian (2-bytes) packet = bytearray([0x26, 0x00]) # 0x26 = IR, 0x00 = no repeats packet += bytearray(struct.pack(' 0: index = 2 + 2 * (pos + bits-i) self.codes[index] = ZERO_AND_ONE_HIGH if test_bit(value, i - 1) != 0: self.codes[index + 1] = ONE_LOW else: self.codes[index + 1] = ZERO_LOW if test_bit(value, i - 1) != 0: bitset = 0 bitset = set_bit(bitset, (128 + i - pos - bits - 1) % 4) self.crc = self.crc + bitset i -= 1