def __init__(self, daemon, cfg): super(RS485, self).__init__(daemon, cfg) daemon.knx_read_cbs.append(self.process_knx) daemon.value_direct_cbs.append(self.process_direct) log.debug("{} obj_list: {!r}".format(self.device_name, self.obj_list)) self._reader = None self._writer = None
async def process_knx(self, cmd): msg = None log.debug("avr processes knx command '%r'" % cmd) try: if cmd[0] == 'P': if cmd[1:3] == "on" and self.get_value_by_avr("power") != "on": msg = "PO" elif cmd[1:4] == "off" and self.get_value_by_avr("power") != "off": msg = "PF" elif cmd[0] == 'V': new_vol = int(cmd[1:]) if new_vol != self.get_value_by_avr("volume"): avr_vol = round(new_vol * (185.0 / 255.0)) msg = "%03dVL" % avr_vol self.set_value_for_avr("volume", new_vol) elif cmd[0] == 'F': new_fn = int(cmd[1:3]) if new_fn in range (0,32) and new_fn != self.get_value_by_avr("fn"): msg = "%02dFN" % new_fn self.set_value_for_avr("fn", new_fn) if msg: await self.send_avr(msg) return True except: return False
async def handle_ac(self): log_msg = [] while True: self._client.receive_info() group_value_dict = {} for o in self.obj_list: ac_obj = o["ac_object"] try: value = getattr(self._client, ac_obj) if value == "A": value = 0 elif value == "B": value = 1 except KeyError: continue if value == o["value"]: log_msg.append("{!r}: {!r}".format(ac_obj, value)) continue group_value_dict[o["knx_group"]] = str(value) o["value"] = value if group_value_dict: await self.d.set_group_value_dict(group_value_dict) if log_msg: log.debug(self.device_name + " skipped objects: " + ', '.join(log_msg)) log_msg = [] await asyncio.sleep(self.poll_interval)
def __init__(self, daemon, cfg): super(PioneerAVR, self).__init__(daemon, cfg) self.avr_reader = None self.avr_writer = None self.accu_word = None daemon.knx_read_cbs.append(self.process_knx) log.debug("{} obj_list: {!r}".format(self.device_name, self.obj_list))
async def poll_ups(self): while True: hello = (chr(0) + chr(6) + "status").encode('ascii') log.debug("{} polling APCD: {!r}".format(self.device_name, hello)) self.ups_writer.write(hello) await self.ups_writer.drain() await asyncio.sleep(self.poll_interval)
async def _write_mqtt(self, knx_group, knx_val, debug_msg): if not self._mqtt_client: return objects = [] request_status = False for item in self.obj_list: if item["knx_group"] == knx_group: objects.append(item) request_status = "request_status" in item for o in objects: if "publish_topic" in o: topic = o["publish_topic"] prev_val = o["value"] if "valmap" in o: payload = o["valmap"][knx_val] else: payload = knx_val log.info(f"{debug_msg} topic {topic} updating {prev_val}=>{knx_val} ({payload})") try: await self._mqtt_client.publish(topic, payload, qos=1, retain=True) o["value"] = knx_val except MqttCodeError as error: log.error(f"{debug_msg} MqttCodeError {error} on topic {topic}") if objects and request_status and "status_object" in self.cfg and self._mqtt_client and not knx_group in self.status_pending_for_groups: so = self.cfg["status_object"] delay = so.get("delay", 10.0) topic = so["topic"] payload = so["payload"] await asyncio.sleep(delay) try: await self._mqtt_client.publish(topic, payload, qos=1, retain=True) log.debug(f"{debug_msg} requested status topic {topic} payload=>{payload}") self.status_pending_for_groups.append(knx_group) except MqttCodeError as error: log.error(f"{debug_msg} MqttCodeError {error} on topic {topic}")
async def handle_rs485(self): while True: try: line = await self._reader.readline() cmd = line.decode('utf-8').strip() log.debug(self.device_name + " received: '" + cmd + "'") (key, val) = cmd.split('=') o = self._get_obj_by_key(key) if "receive" in o["enabled"]: if "valmap" in o and val in o["valmap"]: knxval = o["valmap"][val] else: knxval = val await self.d.set_group_value_dict({o["knx_group"]: knxval}) else: log.warning( f"{self.device_name} command key {key} is not a receiving object!" ) except ValueError: log.warning("{} couldn't parse command {!r}!".format( self.device_name, line)) except StopIteration: log.warning( f"{self.device_name} command key {key} not configured!")
async def process_direct(self, knx_group, value): try: o = self.get_obj_by_knxgrp(knx_group) log.debug( f"{self.device_name} update internal state knx_group={knx_group} value={value}" ) o["value"] = value except StopIteration: return
def __init__(self, daemon, cfg): super(DaikinAC, self).__init__(daemon, cfg) daemon.knx_read_cbs.append(self.process_knx) self.poll_interval = "poll_interval" in cfg and cfg[ "poll_interval"] or 10 for obj in cfg["objects"]: if obj["enabled"]: obj.update({"value": None}) log.debug("{} obj_list: {!r}".format(self.device_name, self.obj_list))
async def _card_removed(self, card): log.debug(f"{self.device_name} FOB {card} removed!") key = str(card.value) if key in self.objs_by_fob: for obj in self.objs_by_fob[key]: knx_group = obj["knx_group"] delay = obj["delay"] await asyncio.sleep(delay) log.debug(f"{self.device_name} stopping {knx_group}") await self.d.set_group_value_dict({knx_group: "off"})
async def process_direct(self, group, value): log.debug(f"{self.device_name} process_direct(group={group}, value={value})") try: o = self._get_output_obj_by_knxgrp_and_value(group, value) if o: await self.set_gpio(o, value) else: log.debug(f"{self.device_name} no gpio output found.") except StopIteration: return
async def write_rs485(self, o, value, debug_msg): rs485key = o["rs485key"] if "valmap" in o: val = next( (key for key, val in o["valmap"].items() if val == value), value) else: val = value cmd = (rs485key + '=' + val) log.debug(f"{debug_msg} writing RS485 command {cmd}") self._writer.write((cmd + '\r\n').encode(encoding='ascii')) await self._writer.drain()
async def process_knx(self, cmd): try: knx_grp, raw = cmd.split("=") value = raw.strip() log.debug(f"{self.device_name} process_knx(group={knx_grp}, value={value})") o = self._get_output_obj_by_knxgrp_and_value(knx_grp, value) if o: await self.set_gpio(o, value) else: log.debug(f"{self.device_name} no gpio output found.") return True except: return False
def receive_info(self): try: self._cached_ctrl_fields = self._get( "/aircon/get_control_info") log.debug("Daikin AC Control Fields {!r}".format( self._cached_ctrl_fields)) self._cached_sens_fields = self._get( "/aircon/get_sensor_info") log.debug("Daikin AC Sensor Fields {!r}".format( self._cached_sens_fields)) except ConnectionError as e: log.warning( "Daikin AC Couldn't perform API read {!r}".format(e))
async def send_knx(self, sequence): async with self._knx_lock: xml = '<write>' + sequence + '</write>\n\x04' log.debug("sending to knx:{!r}".format(xml)) self.knx_client_writer.write(xml.encode(encoding='utf_8')) await self.knx_client_writer.drain() data = await asyncio.wait_for(self.knx_client_reader.readline(), timeout=30.0) decoded = data.decode() if "<write status='error'>" in decoded: log.error("LinKNX {}".format(decoded[1:-1])) else: log.debug("LinKNX {!r}".format(decoded))
async def process_values(self, query): group_value_dict = {} for obj in self.obj_list: sensor = obj["sensor"] group = obj["knx_group"] debug_msg = "%s->%s" % (sensor, group) if sensor in query and obj["enabled"]: try: value = float(query[sensor]) if value == -9999: log.debug( "bogus value for {}, ignored!".format(debug_msg)) continue debug_msg += " numeric value: {0:g}".format(value) conversion = obj["conversion"] if conversion and conversion in self.Unit_converter: value = round(self.Unit_converter[conversion](value), 2) debug_msg += "^={0:g}".format(value) hysteresis = obj["hysteresis"] prev_val = obj["value"] if type(hysteresis) == str and "%" in hysteresis and abs( value - prev_val) <= float( hysteresis.strip('%')) * value * 0.01 or type( hysteresis) == float and abs( value - prev_val) <= hysteresis: log.debug( "{0}-{1:g} < {2:g} hysteresis, ignored!".format( debug_msg, prev_val, hysteresis)) continue elif prev_val == value: log.debug("{!r} unchanged, ignored!".format(debug_msg)) continue group_value_dict[group] = "%.2f" % value except ValueError: value = query[sensor] debug_msg += " non-numeric value:", value if group in self.previous_values and value == self.previous_values[ group]: log.debug("{!r} unchanged, ignored!".format(debug_msg)) continue group_value_dict[group] = value obj["value"] = value log.debug(debug_msg) if group_value_dict: await self.d.set_group_value_dict(group_value_dict)
async def knx_server_handler(self, reader, writer): data = await reader.readline() cmd = data.decode() addr = writer.get_extra_info('peername') log.debug("Received %r from %r" % (cmd, addr)) parse_errors = [] for callback in self.knx_read_cbs: if not await callback(cmd): parse_errors.append(callback) if parse_errors: log.error( "Couldn't parse linknx command: {!r} in callback {!r}".format( cmd, parse_errors)) writer.close()
async def handle(self, request): query = request.rel_url.query log.debug(f"{self.device_name} handle: {query!r}") if "toggle" in query: if not "key" in query: log.warning( f"{self.device_name} no FOB key given when handling {query!r}!" ) return web.Response(status=403, text="Forbidden\n") fobkey = query["key"] fobname = self.d.cfg["fobs"].get(fobkey, "unknown") for item in self.obj_list: if "opener" in item: opener = item if "lock" in item: lock = item if lock["value"] == "close": if fobkey in lock["allowed_fobs"]: log.warning( f"{self.device_name} {fobname}' FOB ({fobkey}) validated for unlocking!" ) await self.d.set_group_value_dict( {lock["knx_group"]: "open"}) lockdelay = lock["delay"] await asyncio.sleep(lock["delay"]) log.warning( f"{self.device_name} lockdelay waited for {lockdelay}s" ) else: log.warning( f"{self.device_name} {fobname}' FOB ({fobkey}) not allowed to unlock!" ) return web.Response(status=403, text="Forbidden\n") if fobkey in opener["allowed_fobs"]: log.info( f"{self.device_name} {fobname}'s FOB validated ({fobkey}) for opening!" ) await self.d.set_group_value_dict({opener["knx_group"]: "on"}) await asyncio.sleep(opener["delay"]) await self.d.set_group_value_dict({opener["knx_group"]: "off"}) return web.Response(text="success\n") else: log.warning( f"{self.device_name} {fobname}'s FOB ({fobkey}) not allowed to open!" ) return web.Response(status=403, text="Forbidden\n") return web.Response(status=404, text="Not found\n")
def __init__(self, cfg, daemon): super(Rdm6300Reader, self).__init__(cfg["serialDevice"]) self.d = daemon self.device_name = cfg["name"] self.fobs = daemon.cfg["fobs"] self.forbidden_fobs = daemon.cfg["forbidden_fobs"] self.throttle_delay = "throttle_delay" in cfg and cfg( "throttle_delay") or 5 self.objs_by_fob = {} for o in cfg["objects"]: for key in o["allowed_fobs"]: if o["enabled"]: if key not in self.objs_by_fob: self.objs_by_fob[key] = [] d = {"knx_group": o["knx_group"], "delay": o["delay"]} self.objs_by_fob[key].append(d) log.debug(f"{self.device_name} objs_by_fob: {self.objs_by_fob!r}")
async def handle_sm(self): log.debug('handle_sm...') from pymodbus.constants import Endian from pymodbus.payload import BinaryPayloadDecoder self._BE = Endian.Big if self.cfg["modbus_wordorder"] == "LE": self.wordorder = Endian.Little else: self.wordorder = Endian.Big self.modbus_bin_pay_dec = BinaryPayloadDecoder while True: group_value_dict = {} for o in self.obj_list: register = o["register"] raw_val = self._read_modbus(o["data_type"], o["register"]) if raw_val is None: continue prev_val = o["value"] mag = o["magnitude"] value = round(raw_val * o["magnitude"], 3) debug_msg = "{} read {} raw={} => value={}".format( self.device_name, o["knx_group"], raw_val, value) hysteresis = o["hysteresis"] if type(hysteresis) == str and "%" in hysteresis and abs( value - prev_val) <= float(hysteresis.strip( '%')) * value * 0.01 or type(hysteresis) in ( int, float) and abs(value - prev_val) <= hysteresis: log.debug("{}-{} < {} hysteresis, ignored!".format( debug_msg, prev_val, hysteresis)) continue elif prev_val == value: log.debug("{} unchanged, ignored!".format(debug_msg)) continue else: log.debug("{} UPDATED from {}".format(debug_msg, prev_val)) prec = o["precision"] str_value = "%.{}f".format(prec) % round(value, prec) group_value_dict[o["knx_group"]] = str_value o["value"] = value if group_value_dict: await self.d.set_group_value_dict(group_value_dict) await asyncio.sleep(self.poll_interval)
def __init__(self, daemon, cfg): super(gpio, self).__init__(daemon, cfg) daemon.knx_read_cbs.append(self.process_knx) daemon.value_direct_cbs.append(self.process_direct) for o in cfg["objects"]: gpio_type = o["gpio_type"] gpio_pin = o["gpio_pin"] if o["enabled"] and gpio_type in self.GPIO_TYPE_MAP: gpio_obj = self.GPIO_TYPE_MAP[gpio_type][0](gpio_pin) o["gpio_object"] = gpio_obj o["gpio_direction"] = self.GPIO_TYPE_MAP[gpio_type][1] for key, val in o["actions"].items(): if o["gpio_direction"] == self.GPIO_DIRECTION_INPUT: action = key value = val elif o["gpio_direction"] == self.GPIO_DIRECTION_OUTPUT: action = val value = key if action in self.GPIO_ACTION_MAP: actioncbname = self.GPIO_ACTION_MAP[action] if hasattr(gpio_obj, actioncbname): actioncb = (lambda value: lambda evt: self.gpio_action(evt, o["knx_group"], value))(value) if o["gpio_direction"] == self.GPIO_DIRECTION_INPUT: setattr(gpio_obj, actioncbname, actioncb) log.debug(f"{self.device_name} init. on callback='{actioncbname}' set value='{value}' for {o!r}") else: o["set_cb"] = getattr(gpio_obj, actioncbname) log.debug(f"{self.device_name} init. on value change to '{value}', call='{actioncbname}' for {o!r}") else: log.warning(f"{self.device_name} init illegal action '{actioncbname}' for {gpio_obj!r}") log.debug("{} obj_list: {!r}".format(self.device_name, self.obj_list))
async def mqtt_handle(self, messages, obj): group_value_dict = {} async for message in messages: payload = message.payload.decode() try: jsonobj = loads(payload) except ValueError: jsonobj = None value = str(payload) objects = self._get_objects_by_topic(message.topic) for o in objects: knx_group = o["knx_group"] if jsonobj and "valmap" in o: for key, valdict in o["valmap"].items(): if key in jsonobj: prop = jsonobj[key] if type(valdict) == dict and prop in valdict: value = valdict[prop] else: value = str(prop) else: value = None continue prev_val = o["value"] if value == None: pass elif prev_val != value: o["value"] = value group_value_dict[knx_group] = value log.debug(f"{self.device_name} mqtt topic={message.topic} payload={payload} knx_group={knx_group} updated value {prev_val}=>{value}") if group_value_dict: await self.d.set_group_value_dict(group_value_dict) else: log.debug(f"{self.device_name} mqtt topic={message.topic} payload={payload} knx_group={knx_group} value={value} unchanged, ignored") if knx_group in self.status_pending_for_groups: self.status_pending_for_groups.remove(knx_group)
async def set_gpio(self, o, value): log.debug(f"{self.device_name} set_gpio({o!r}, {value})") try: gpio_obj = o["gpio_object"] prev_value = gpio_obj.value set_cb = o["set_cb"] log.info(f"{self.device_name} set_gpio prev_active={prev_value}->{value}. calling {set_cb!r}...") set_cb() if "actuate_seconds" in o: delay = o["actuate_seconds"] log.debug(f"{self.device_name} waiting {delay} s") await asyncio.sleep(delay) gpio_obj.value = prev_value log.debug(f"{self.device_name} restored previous state") except (TypeError, AttributeError): log.warning(f"{self.device_name} set_gpio illegal action '{value}' for {gpio_obj!r}")
async def process_knx(self, cmd): msg = None try: knx_grp, raw = cmd.split("=") debug_msg = "{} knx group {} raw={}".format( self.device_name, knx_grp, raw) try: ac_obj = self.get_obj_by_knxgrp(knx_grp)["ac_object"] except StopIteration: log.debug( "{} no AC object for given KNX group, ignored".format( debug_msg)) return True if ac_obj == "fan_rate": if raw == "0": value = "A" elif raw == "1": value = "B" elif raw in ["off", "false", "disable", "stop", "inactive"]: value = 0 elif raw in ["on", "true", "enable", "start", "active"]: value = 1 else: value = raw if str(value) == str(self._get_value_by_acobj(ac_obj)): log.debug( "{} ac_obj {} unchanged value {}->{}, ignored!".format( debug_msg, ac_obj, str(self._get_value_by_acobj(ac_obj)), str(value))) return True log.debug("{} ac_obj {} updated value {}=>{}".format( debug_msg, ac_obj, self._get_value_by_acobj(ac_obj), value)) setattr(self._client, ac_obj, value) return True except: return False
async def handle(self, request): log.debug("handle: {!r}".format(request.rel_url.query)) await self.process_values(request.rel_url.query) return web.Response(text="success\n")
async def handle_ups(self): while True: data = await self.ups_reader.readuntil(b'\x00\x00') debug_msg = [] if not data: break data = data.decode('ascii') m = re.match(self.expression, data, re.DOTALL) if m: group_value_dict = {} for idx, o in enumerate(self.obj_list): group = o["knx_group"] val = m.groups(0)[idx] debug_line = "idx: {} group: {} val: {}".format( idx, group, val) prev_val = o["value"] try: value = float(val) debug_line += " numeric value: {0:g}".format(value) hysteresis = o["hysteresis"] if type(hysteresis ) == str and "%" in hysteresis and abs( value - prev_val) <= float( hysteresis.strip('%') ) * value * 0.01 or type( hysteresis) == float and abs( value - prev_val) <= hysteresis: debug_msg.append( "{}-{:g} < {:g} hysteresis, ignored!".format( debug_line, prev_val, hysteresis)) continue elif prev_val == value: debug_msg.append( "{} unchanged, ignored!".format(debug_line)) continue group_value_dict[group] = "%.2f" % value except ValueError: if val == "ONLINE": value = "true" elif val == "ONBATT": value = "false" debug_line += " non-numeric value: ->{}".format(value) if prev_val == value: debug_msg.append( "{} unchanged, ignored!".format(debug_line)) continue group_value_dict[group] = value o["value"] = value debug_msg.append(debug_line) log.debug("{} {!r}\t".format(self.device_name, data) + "\n\t".join(debug_msg)) if group_value_dict: await self.d.set_group_value_dict(group_value_dict) else: log.warning("{} Couldn't parse {!r}".format( self.device_name, data))
async def send_avr(self, data): log.debug("sending to avr: '%s'" % data) self.avr_writer.write((data+'\r').encode(encoding='ascii')) self.avr_writer.drain()
async def handle_avr(self): while True: group_value_dict = {} data = await self.avr_reader.readline() if not data: break line = data.decode('ascii') log.debug('avr received {!r}'.format(line)) if line.startswith('FL02'): if line == "FL022020202020202020202020202020\r\n": self.accu_word = self.accu_word and self.accu_word.rstrip() or "" self.set_value_for_avr("display_text", self.accu_word) group_value_dict[self.get_knx_by_avr("display_text")] = self.get_value_by_avr("display_text") log.debug("display_text complete! '%s'" % self.get_value_by_avr("display_text")) else: new_word = bytes.fromhex(line[4:-2]).decode('iso8859_15') if self.accu_word == None: self.accu_word = new_word log.debug("1START new_word={!r} accu_word={!r}".format(new_word, self.accu_word)) elif self.accu_word[-13:] != new_word[:-1]: if not self.get_value_by_avr("display_text") or new_word not in self.get_value_by_avr("display_text"): self.set_value_for_avr("display_text", None) self.accu_word = new_word group_value_dict[self.get_knx_by_avr("display_text")] = self.accu_word log.debug("CHANGE new_word={!r} accu_word={!r}".format(new_word, self.accu_word)) else: log.debug("STARTOVER new_word={!r} accu_word={!r}".format(new_word, self.accu_word)) else: self.accu_word += new_word[-1:] log.debug("+++++ new_word={!r} accu_word={!r}".format(new_word, self.accu_word)) if not self.get_value_by_avr("display_text") and self.accu_word[-1] != ' ': group_value_dict[self.get_knx_by_avr("display_text")] = self.accu_word.rstrip() log.debug("COMMIT new_word={!r} accu_word={!r}".format(new_word, self.accu_word)) elif line.startswith('VOL'): avr_volume = int(line[3:]) new_volume = round(avr_volume * (255.0 / 185.0)) if new_volume != self.get_value_by_avr("volume"): self.set_value_for_avr("volume", new_volume) group_value_dict[self.get_knx_by_avr("volume")] = self.get_value_by_avr("volume") elif line.startswith('PWR'): if line[3] == '0': self.set_value_for_avr("power", "on") else: self.set_value_for_avr("power", "off") group_value_dict[self.get_knx_by_avr("power")] = self.get_value_by_avr("power") self.set_value_for_avr("display_text", None) group_value_dict[self.get_knx_by_avr("display_text")] = self.get_value_by_avr("display_text") elif line.startswith('FN'): new_fn = int(line[2:4]) if new_fn != self.get_value_by_avr("fn"): self.set_value_for_avr("fn", new_fn) group_value_dict[self.get_knx_by_avr("fn")] = self.get_value_by_avr("fn") self.set_value_for_avr("display_text", None) group_value_dict[self.get_knx_by_avr("display_text")] = self.get_value_by_avr("display_text") if group_value_dict: await self.d.set_group_value_dict(group_value_dict)