Exemple #1
0
def rule_openhab_sensor(obj=m.Sensor(), change=None):
    if obj.sensor_name is None:
        L.l.warning('Got empty openhab sensor name {}'.format(obj))
        return
    key = 'temperature'
    if hasattr(obj, key) and obj.temperature is not None:
        send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name,
                          payload=obj.temperature)
    key = 'humidity'
    if hasattr(obj, key) and obj.humidity is not None:
        send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name,
                          payload=obj.humidity)
    key = 'pressure'
    if hasattr(obj, key) and obj.pressure is not None:
        send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name,
                          payload=obj.pressure)
    key = 'vad'
    if hasattr(obj, key) and obj.vad is not None:
        send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name,
                          payload=obj.vad)
    key = 'vdd'
    if hasattr(obj, key) and obj.vdd is not None:
        send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name,
                          payload=obj.vdd)
    key = 'iad'
    if hasattr(obj, key) and obj.iad is not None:
        send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name,
                          payload=obj.iad)
Exemple #2
0
def __save_sensor_db(p_id='', p_type='', value_list=None):
    if not value_list:
        value_list = []

    record = m.Sensor.find_one({m.Sensor.address: p_id})
    if record is None:
        record = m.Sensor()
        record.address = p_id
    zone_sensor = m.ZoneSensor.find_one({m.ZoneSensor.sensor_address: p_id})
    if zone_sensor:
        record.sensor_name = zone_sensor.sensor_name
    else:
        record.sensor_name = '(not defined) ' + p_id
    record.updated_on = utils.get_base_location_now_date()
    record.type = p_type
    if 'Humidity' in value_list:
        record.humidity = utils.round_sensor_value(value_list['Humidity'])
    if 'Temperature' in value_list:
        record.temperature = utils.round_sensor_value(
            value_list['Temperature'])
    if 'Battery numeric' in value_list:
        record.battery_level = value_list['Battery numeric']
    if 'Rssi numeric' in value_list:
        record.rssi = value_list['Rssi numeric']
    # L.l.info('Saving RFX object {}'.format(record))
    record.save_changed_fields(broadcast=True, persist=True)
Exemple #3
0
def _get_sensor(sensor_address, sensor_type):
    zone_sensor, actual_sensor_name = _get_zone_sensor(sensor_address, sensor_type)
    sensor = m.Sensor.find_one({m.Sensor.address: sensor_address})
    if sensor is None:
        sensor = m.Sensor()
        sensor.address = sensor_address
        sensor.sensor_name = actual_sensor_name
    return zone_sensor, sensor
Exemple #4
0
def rule_sensor_temp_extreme(obj=m.Sensor(), change=None):
    if hasattr(obj, 'temperature') and obj.temperature is not None:
        zonesensor = m.ZoneSensor.find_one(
            {m.ZoneSensor.sensor_name: obj.sensor_name})
        if zonesensor is not None and zonesensor.target_material is not None:
            zone = m.Zone.find_one({m.Zone.id: zonesensor.zone_id})
            if zone is not None:
                max = min = None
                if zone.is_indoor:
                    location = 'indoor'
                elif zone.is_indoor_heated:
                    location = 'indoor_heated'
                elif zone.is_outdoor:
                    location = 'outdoor'
                elif zone.is_outdoor_heated:
                    location = 'outdoor_heated'
                else:
                    L.l.warning(
                        "Zone {} has no indoor/outdoor location set".format(
                            zone.name))
                    return False

                max_temp = TempStore.max_temp[location]
                if zonesensor.target_material in max_temp:
                    max = max_temp[zonesensor.target_material]
                else:
                    L.l.warning("Unknown max target material {}".format(
                        zonesensor.target_material))
                min_temp = TempStore.min_temp[location]
                if zonesensor.target_material in min_temp:
                    min = min_temp[zonesensor.target_material]
                else:
                    L.l.warning("Unknown min target material {}".format(
                        zonesensor.target_material))

                if max is not None and obj.temperature >= max:
                    rule_common.notify_via_all(
                        title="Max temperature reached for {} is {}".format(
                            obj.sensor_name, obj.temperature),
                        message="!",
                        priority=1)
                if min is not None and obj.temperature <= min:
                    rule_common.notify_via_all(
                        title="Min temperature reached for {} is {}".format(
                            obj.sensor_name, obj.temperature),
                        message="!",
                        priority=1)
            else:
                L.l.warning(
                    "Cannot find a zone for zone_sensor {}".format(zonesensor))
    return True
Exemple #5
0
def set_value(network, node, value):
    try:
        # L.l.info('Louie set_value signal: Node={} Value={}'.format(node, value))
        sensor_address = "{}_{}".format(node.product_name, node.node_id)
        zone_sensor = m.ZoneSensor.find_one(
            {m.ZoneSensor.sensor_address: sensor_address})
        if zone_sensor is not None:
            sensor_name = zone_sensor.sensor_name
            P.last_value_received = datetime.now()
            if value.label == "Switch":
                _set_custom_relay_state(sensor_address=sensor_address,
                                        state=value.data)
            elif value.label == "Power":
                if value.units == "W":
                    units_adjusted = "watt"  # this should match Utility unit name in models definition
                    value_adjusted = round(value.data, 0)
                    if abs(value_adjusted) < P.MAX_SENSOR_POWER_WATTS:
                        haiot_dispatch.send(Constant.SIGNAL_UTILITY_EX,
                                            sensor_name=sensor_name,
                                            value=value_adjusted,
                                            unit=units_adjusted)
                    else:
                        L.l.warning(
                            "Faulty power reading, value={}, cap={}".format(
                                value_adjusted, P.MAX_SENSOR_POWER_WATTS))
                else:
                    L.l.warning("Unknown power units {} for sensor {}".format(
                        value.units, sensor_name))
                # L.l.info("Saving power utility {} {} {}".format(sensor_name, value.units, value.data))
            elif value.label == "Energy" and value.units == "kWh":
                haiot_dispatch.send(Constant.SIGNAL_UTILITY_EX,
                                    sensor_name=sensor_name,
                                    value=value.data,
                                    unit=value.units)
                # L.l.info("Saving energy utility {} {} {}".format(sensor_name, value.units, value.data))
            else:
                # skip controller node
                if node.node_id > 1:
                    record = m.Sensor.find_one(
                        {m.Sensor.address: sensor_address})
                    delta_last_save = P.DELTA_SAVE_SECONDS
                    if record is None:
                        L.l.info("Zwave sensor address not found:[{}]".format(
                            sensor_address))
                        record = m.Sensor()
                        record.address = sensor_address
                        record.sensor_name = zone_sensor.sensor_name
                        record.updated_on = datetime.now()
                    else:
                        if record.updated_on is not None:
                            delta_last_save = (
                                datetime.now() -
                                record.updated_on).total_seconds()
                    record.is_event_external = True
                    if value.label == "Voltage":
                        if abs(value.data) < P.MAX_SENSOR_VOLTAGE:
                            record.vad = round(value.data, 0)
                            record.save_changed_fields(broadcast=False,
                                                       persist=True)
                        else:
                            L.l.warning(
                                "Faulty voltage reading, value={}, cap={}".
                                format(value.data, P.MAX_SENSOR_VOLTAGE))
                        # L.l.info("Saving voltage {} {}".format(sensor_name, value.data))
                    elif value.label == "Current":
                        record.iad = round(value.data, 1)
                        record.save_changed_fields(broadcast=False,
                                                   persist=True)
                        # L.l.info("Saving current {} {}".format(sensor_name, value.data))
                    elif value.label == "Power Factor":
                        if abs(value.data) < P.MAX_POWER_FACTOR:
                            record.vdd = round(value.data, 1)
                            record.save_changed_fields(broadcast=False,
                                                       persist=True)
                        else:
                            L.l.warning(
                                "Faulty pf reading, value={}, cap={}".format(
                                    value.data, P.MAX_POWER_FACTOR))
                        # L.l.info("Saving power factor {} {}".format(sensor_name, value.data))
                    else:
                        # L.l.warning("Doing nothing on zwave set value {}".format(value))
                        pass
        else:
            L.l.info(
                "Cannot find zwave sensor in db, address={} node={} value={}".
                format(sensor_address, node, value))
    except Exception as ex:
        L.l.error("Error in zwave value={}".format(ex), exc_info=True)
Exemple #6
0
def rule_sensor_temp_target(obj=m.Sensor(), change=None):
    if not change:
        field_changed_list = []
    #temp = obj.temperature
    return 'rule temp ok'
Exemple #7
0
def save_to_db(dev):
    try:
        delta_time_counters = None
        address = dev['address']
        zone_sensor = m.ZoneSensor.find_one(
            {m.ZoneSensor.sensor_address: address})
        record = m.Sensor.find_one({m.Sensor.address: address})
        if record is None:
            record = m.Sensor()
            record.address = address
        if zone_sensor:
            record.sensor_name = zone_sensor.sensor_name
        else:
            record.sensor_name = '{} {} {}'.format(address, dev['n_address'],
                                                   dev['type'])
        record.type = dev['type']
        record.updated_on = utils.get_base_location_now_date()
        record.comment = dev['path']
        if 'counters_a' in dev.keys():
            if record.counters_a is not None:
                record.delta_counters_a = dev['counters_a'] - record.counters_a
            else:
                record.delta_counters_a = 0  # don't know prev. count, assume no consumption (ticks could be lost)
            if record.updated_on is not None:
                # get accurate interval (e.g. to establish power consumption in watts)
                delta_time_counters = (utils.get_base_location_now_date() -
                                       record.updated_on).total_seconds()
            else:  # when running first time with db empty
                delta_time_counters = P.sampling_period_seconds
        if 'counters_b' in dev.keys():
            if record.counters_b is not None:
                record.delta_counters_b = dev['counters_b'] - record.counters_b
            else:
                # fixme: don't know prev. count, assume no consumption (ticks could be lost)
                record.delta_counters_b = 0
        if 'temperature' in dev.keys():
            record.temperature = dev['temperature']
        if 'humidity' in dev.keys():
            record.humidity = dev['humidity']
        if 'iad' in dev.keys():
            record.iad = dev['iad']
        if 'vad' in dev.keys():
            record.vad = dev['vad']
        if 'vdd' in dev.keys():
            record.vdd = dev['vdd']
        if 'pio_a' in dev.keys():
            record.pio_a = dev['pio_a']
        if 'pio_b' in dev.keys():
            record.pio_b = dev['pio_b']
        if 'sensed_a' in dev.keys():
            record.sensed_a = dev['sensed_a']
        if 'sensed_b' in dev.keys():
            record.sensed_b = dev['sensed_b']
        # force field changed detection for delta_counters to enable save in history
        # but allow one 0 record to be saved for nicer graphics
        record.save_changed_fields(broadcast=False, persist=True)
        if record.delta_counters_a is not None or record.delta_counters_b is not None:
            dispatcher.send(Constant.SIGNAL_UTILITY,
                            sensor_name=record.sensor_name,
                            units_delta_a=record.delta_counters_a,
                            units_delta_b=record.delta_counters_b,
                            total_units_a=record.counters_a,
                            total_units_b=record.counters_b,
                            sampling_period_seconds=delta_time_counters)
        if record.vad is not None:
            dispatcher.send(Constant.SIGNAL_UTILITY_EX,
                            sensor_name=record.sensor_name,
                            value=record.vad)
    except Exception as ex:
        L.l.error('Error saving sensor to DB, err {}'.format(ex),
                  exc_info=True)
Exemple #8
0
def thread_run():
    if not P.initialised:
        init_solar_aps()
    if P.initialised:
        try:
            aps_text = str(
                urllib.request.urlopen(
                    get_json_param(Constant.P_SOLAR_APS_LOCAL_URL)).read())
            production = utils.parse_text(aps_text, P.start_keyword,
                                          P.end_keyword)
            last_power = utils.parse_text(aps_text, P.start_keyword_now,
                                          P.end_keyword_now)
            temperature = utils.parse_text(aps_text,
                                           P.start_key_temp,
                                           P.end_key_temp,
                                           end_first=True)
            panel_id = utils.parse_text(aps_text,
                                        P.start_key_panel,
                                        P.end_key_panel,
                                        end_first=True)
            utility_name = get_json_param(Constant.P_SOLAR_UTILITY_NAME)
            if temperature is not None:
                zone_sensor = m.ZoneSensor.find_one(
                    {m.ZoneSensor.sensor_address: panel_id})
                if zone_sensor is None:
                    L.l.warning(
                        'Solar panel id {} is not defined in zone sensor list'.
                        format(panel_id))
                record = m.Sensor.find_one({m.Sensor.address: panel_id})
                if record is None:
                    record = m.Sensor()
                    record.address = panel_id
                    record.type = 'solar'
                    if zone_sensor:
                        record.sensor_name = zone_sensor.sensor_name
                    else:
                        record.sensor_name = record.type + panel_id
                record.temperature = temperature
                record.updated_on = utils.get_base_location_now_date()
                # fixme: keeps saving same temp even when panels are off. stop during night.
                record.save_changed_fields(broadcast=True, persist=True)
            if production is not None:
                production = float(production)
                record = m.Utility.find_one(
                    {m.Utility.utility_name: utility_name})
                if record is None:
                    record = m.Utility()
                    record.utility_name = utility_name
                    record.units_delta = production
                    record.units_total = production
                else:
                    if record.units_total is None:
                        record.units_total = production
                        record.units_delta = 0
                    else:
                        record.units_delta = production - record.units_total
                        if record.units_delta == 0:
                            # do not waste db space if no power generated
                            L.l.info(
                                'Solar production is energy={} watts={} temp={}'
                                .format(production, last_power, temperature))
                            # return
                        else:
                            L.l.info('Solar production is {}'.format(
                                record.units_delta))
                record.units_2_delta = last_power
                # L.l.info('Solar watts is {}'.format(last_power))
                if record.unit_cost is None:
                    record.unit_cost = 0.0
                record.cost = 1.0 * record.units_delta * record.unit_cost
                record.save_changed_fields(broadcast=True, persist=True)
            else:
                L.l.info('Solar production is none')
        except Exception as ex:
            L.l.error("Got exception on solar thread run, ex={}".format(ex),
                      exc_info=True)
Exemple #9
0
def _process_message(msg):
    # L.l.info("Topic={} payload={}".format(msg.topic, msg.payload))
    if '/SENSOR' in msg.topic or '/RESULT' in msg.topic:
        topic_clean = P.sonoff_topic.replace('#', '')
        if topic_clean in msg.topic:
            try:
                sensor_name = msg.topic.split(topic_clean)[1].split('/')[1]
                obj = utils.json2obj(transport.mqtt_io.payload2json(msg.payload))
            except Exception as ex:
                L.l.error("Error decoding sensor name, topic={} payload={}".format(msg.topic, msg.payload))
                return False

            if obj is None:
                L.l.error("Unable to decode tasmota {}".format(msg.payload))
                return False
            #################################################################

            if 'ENERGY' in obj:
                energy = obj['ENERGY']
                power = float(energy['Power'])
                if 'Voltage' in energy:
                    voltage = int(energy['Voltage'])
                else:
                    voltage = None
                if 'Factor' in energy:
                    factor = energy['Factor']
                else:
                    factor = None
                if 'Current' in energy:
                    current = float(energy['Current'])
                else:
                    current = None
                if 'Today' in energy:
                    today_energy = energy['Today']
                else:
                    today_energy = None
                # unit should match Utility unit name in models definition
                dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=power, unit='watt')
                # todo: save total energy utility
                if voltage or factor or current:
                    zone_sensor = m.ZoneSensor.find_one({m.ZoneSensor.sensor_address: sensor_name})
                    if zone_sensor is not None:
                        record = m.Sensor.find_one({m.Sensor.address: sensor_name})
                        if record is None:
                            record = m.Sensor()
                            record.address = sensor_name
                            record.sensor_name = zone_sensor.sensor_name
                        record.vad = None
                        record.iad = None
                        record.vdd = None
                        if voltage is not None:
                            record.vad = round(voltage, 0)
                        if current is not None:
                            record.iad = round(current, 1)
                        if factor is not None:
                            record.vdd = round(factor, 1)
                        if voltage is not None or current is not None or factor is not None:
                            record.save_changed_fields(broadcast=False, persist=True)
                # dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=current, unit='kWh')
            # check for single relay
            if 'POWER' in obj:
                power_is_on = obj['POWER'] == 'ON'
                # relay = m.ZoneCustomRelay.find_one({m.ZoneCustomRelay.gpio_pin_code: sensor_name,
                #                                    m.ZoneCustomRelay.gpio_host_name: Constant.HOST_NAME})
                relay = m.ZoneCustomRelay.find_one({m.ZoneCustomRelay.gpio_pin_code: sensor_name})
                if relay is not None:
                    L.l.info("Got single relay {} state={}".format(sensor_name, power_is_on))
                    relay.relay_is_on = power_is_on
                    # set listeners to false as otherwise on reboot sonoff relays that were on will power- cycle
                    relay.save_changed_fields(broadcast=False, persist=True, listeners=False)
                else:
                    L.l.warning("ZoneCustomRelay single {} not defined in db".format(sensor_name))
            # check for multiple relays
            multiple_relays_list = []
            if 'POWER1' in obj:
                multiple_relays_list.append(1)
            if 'POWER2' in obj:
                multiple_relays_list.append(2)
            if len(multiple_relays_list) > 0:
                for index in multiple_relays_list:
                    power_is_on = obj['POWER' + str(index)] == 'ON'
                    relay = m.ZoneCustomRelay.find_one({m.ZoneCustomRelay.gpio_pin_code: sensor_name,
                                                        m.ZoneCustomRelay.relay_index: index})
                    if relay is not None:
                        L.l.info("Got multiple relay {} state={}".format(sensor_name, power_is_on))
                        relay.relay_is_on = power_is_on
                        # set listeners to false as otherwise on reboot sonoff relays that were on will power- cycle
                        relay.save_changed_fields(broadcast=False, persist=True, listeners=False)
                    else:
                        L.l.warning("ZoneCustomRelay multiple {} not defined in db".format(sensor_name))
            if 'COUNTER' in obj:
                # TelePeriod 60
                counter = obj['COUNTER']
                for i in [1, 2, 3, 4, 5, 6, 7, 8]:
                    c = 'C{}'.format(i)
                    if c in counter:
                        cval = int(counter[c])
                        dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=cval, index=i)

            # iot/sonoff/tele/sonoff-basic-3/SENSOR =
            # {"Time":"2018-10-28T08:12:26","BMP280":{"Temperature":24.6,"Pressure":971.0},"TempUnit":"C"}
            # "BME280":{"Temperature":24.1,"Humidity":39.2,"Pressure":980.0},"PressureUnit":"hPa","TempUnit":"C"}
            # {"BME680":{"Temperature":29.0,"Humidity":63.3,"Pressure":981.6,"Gas":24.46},"PressureUnit":"hPa","TempUnit":"C"}
            # "MHZ19B":{"Model":"B","CarbonDioxide":473,"Temperature":26.0},"TempUnit":"C"
            for k, v in obj.items():
                if k.startswith('BME') or k.startswith('BMP') or k.startswith('MHZ19') or k.startswith('DS18B20'):
                    sensor_address = '{}_{}'.format(sensor_name, k.lower())
                    zone_sensor, sensor = _get_air_sensor(sensor_address=sensor_address, sensor_type=k)
                    if 'Temperature' in v:
                        sensor.temperature = v['Temperature']
                    if 'Pressure' in v:
                        sensor.pressure = v['Pressure']
                    if 'Humidity' in v:
                        if 0 < v['Humidity'] < 100:
                            sensor.humidity = v['Humidity']
                    if 'Gas' in v:
                        sensor.gas = v['Gas']
                    if 'CarbonDioxide' in v:
                        val = v['CarbonDioxide']
                        if val > 0:
                            sensor.co2 = val
                    sensor.save_changed_fields(broadcast=False, persist=True)

                if k.startswith('INA219'):
                    ina = v  # obj['INA219']
                    # multiple ina sensors
                    if '-' in k:
                        index = k.split('-')[1]
                        sensor = m.PowerMonitor.find_one(
                            {m.PowerMonitor.host_name: sensor_name, m.PowerMonitor.type: "ina{}".format(index)})
                    else:
                        sensor = m.PowerMonitor.find_one({m.PowerMonitor.host_name: sensor_name})
                    if sensor is None:
                        L.l.warning('Sensor INA on {} not defined in db'.format(sensor_name))
                    else:
                        if 'Voltage' in ina:
                            voltage = ina['Voltage']
                            sensor.voltage = voltage
                        if 'Current' in ina:
                            current = ina['Current']
                            sensor.current = current
                        if 'Power' in ina:
                            power = ina['Power']
                            sensor.power = power
                        sensor.save_changed_fields(broadcast=False, persist=True)
            if 'ANALOG' in obj:
                # "ANALOG":{"A0":7}
                an = obj['ANALOG']
                a0 = an['A0']
                sensor_address = '{}_{}'.format(sensor_name, 'a0')
                zone_sensor, sensor = _get_sensor(sensor_address=sensor_address, sensor_type='ANALOG')
                sensor.vad = a0
                sensor.save_changed_fields(broadcast=False, persist=True)
            if 'PMS5003' in obj:
                # "PMS5003":{"CF1":0,"CF2.5":1,"CF10":3,"PM1":0,"PM2.5":1,"PM10":3,"PB0.3":444,"PB0.5":120,"PB1":12,
                # "PB2.5":6,"PB5":2,"PB10":2}
                pms = obj['PMS5003']
                sensor_address = '{}_{}'.format(sensor_name, 'pms5003')
                zone_sensor, sensor = _get_dust_sensor(sensor_address=sensor_address, sensor_type='PMS5003')
                sensor.pm_1 = pms['PM1']
                sensor.pm_2_5 = pms['PM2.5']
                sensor.pm_10 = pms['PM10']

                sensor.p_0_3 = pms['PB0.3']
                sensor.p_0_5 = pms['PB0.5']
                sensor.p_1 = pms['PB1']

                sensor.p_2_5 = pms['PB2.5']
                sensor.p_5 = pms['PB5']
                sensor.p_10 = pms['PB10']
                # sometimes first read after power on returns invalid 0 values
                if sensor.pm_1 + sensor.pm_2_5 + sensor.pm_10 + sensor.p_0_3 + sensor.p_0_5 + sensor.p_1 \
                        + sensor.p_2_5 + sensor.p_5 + sensor.p_10 != 0:
                    sensor.save_changed_fields(broadcast=False, persist=True)
            if 'RfReceived' in obj:
                rf = obj['RfReceived']
                sensor_id = rf['Data']
                alarm = m.ZoneAlarm.find_one({m.ZoneAlarm.gpio_pin_code: sensor_id})
                if alarm is not None:
                    dispatcher.send(Constant.SIGNAL_GPIO, gpio_pin_code=sensor_id, pin_connected=False)
                    dispatcher.send(Constant.SIGNAL_GPIO, gpio_pin_code=sensor_id, pin_connected=True)
                else:
                    L.l.warning('Unknown Sonoff RF packet received {}'.format(sensor_id))
        else:
            L.l.warning("Invalid sensor topic {}".format(msg.topic))
    return True