Beispiel #1
0
def __save_sensor_db(p_id='', p_type='', value_list=None):
    if not value_list:
        value_list = []
    record = models.Sensor(address=p_id)
    assert isinstance(record, models.Sensor)
    zone_sensor = models.ZoneSensor.query.filter_by(
        sensor_address=p_id).first()
    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']
    current_record = models.Sensor.query.filter_by(address=p_id).first()
    record.save_changed_fields(current_record=current_record,
                               new_record=record,
                               notify_transport_enabled=True,
                               save_to_graph=True,
                               ignore_only_updated_on_change=True)
Beispiel #2
0
def check_inactive():
    """check for inactive sensors not read recently but in database"""
    sensor_list = models.Sensor().query_all()
    defined_sensor_list = models.ZoneSensor().query_all()
    ref_list = []
    delta = (datetime.datetime.now() - P.last_warning).total_seconds()
    log_warn = (delta > 60 * 15)
    for zone_sensor in defined_sensor_list:
        ref_list.append(zone_sensor.sensor_address)
    for sensor in sensor_list:
        elapsed = round((utils.get_base_location_now_date() -
                         sensor.updated_on).total_seconds() / 60, 0)
        if sensor.address not in ref_list:
            L.l.warning('Sensor {} {} not defined'.format(
                sensor.address, sensor.sensor_name))
            #current_record = models.SensorError.query.filter_by(sensor_address=sensor.address).first()
            #record = models.SensorError()
            #record.sensor_name = sensor.sensor_name
            #if current_record is not None:
            #    record.error_count = current_record.error_count
            #else:
            #    record.error_count = 0
            #record.error_count += 1
            #record.error_type = 0
            #record.save_changed_fields(current_record=None, new_record=record, save_to_graph=True, save_all_fields=True)
        if log_warn and elapsed > 2 * P.sampling_period_seconds:
            L.l.warning('Sensor {} type {} not updated since {} min'.format(
                sensor.sensor_name, sensor.type, elapsed))
            P.last_warning = datetime.datetime.now()
Beispiel #3
0
def record_update(obj):
    # save sensor state to db, except for current node
    try:
        sensor_host_name = utils.get_object_field_value(obj, 'name')
        L.l.debug('Received sensor state update from {}'.format(sensor_host_name))
        # avoid node to update itself in infinite recursion
        if sensor_host_name != Constant.HOST_NAME:
            address = utils.get_object_field_value(obj, 'address')
            n_address = utils.get_object_field_value(obj, 'n_address')
            sensor_type = utils.get_object_field_value(obj, 'type')
            record = models.Sensor(address=address)
            assert isinstance(record, models.Sensor)
            zone_sensor = models.ZoneSensor.query.filter_by(sensor_address=address).first()
            if zone_sensor is not None:
                record.sensor_name = zone_sensor.sensor_name
            else:
                record.sensor_name = '(n/a) {} {} {}'.format(address, n_address, sensor_type)
            record.type = utils.get_object_field_value(obj, 'type')
            record.updated_on = utils.get_base_location_now_date()
            if obj.has_key('counters_a'): record.counters_a = utils.get_object_field_value(obj, 'counters_a')
            if obj.has_key('counters_b'): record.counters_b = utils.get_object_field_value(obj, 'counters_b')
            if obj.has_key('delta_counters_a'):
                record.delta_counters_a = utils.get_object_field_value(obj, 'delta_counters_a')
            if obj.has_key('delta_counters_b'):
                record.delta_counters_b = utils.get_object_field_value(obj, 'delta_counters_b')
            if obj.has_key('temperature'): record.temperature = utils.get_object_field_value(obj, 'temperature')
            if obj.has_key('humidity'): record.humidity = utils.get_object_field_value(obj, 'humidity')
            if obj.has_key('iad'): record.iad = utils.get_object_field_value(obj, 'iad')
            if obj.has_key('vad'): record.vad = utils.get_object_field_value(obj, 'vad')
            if obj.has_key('vdd'): record.vdd = utils.get_object_field_value(obj, 'vdd')
            if obj.has_key('pio_a'): record.pio_a = utils.get_object_field_value(obj, 'pio_a')
            if obj.has_key('pio_b'): record.pio_b = utils.get_object_field_value(obj, 'pio_b')
            if obj.has_key('sensed_a'): record.sensed_a = utils.get_object_field_value(obj, 'sensed_a')
            if obj.has_key('sensed_b'): record.sensed_b = utils.get_object_field_value(obj, 'sensed_b')

            current_record = models.Sensor.query.filter_by(address=address).first()
            # force field changed detection for delta_counters
            if current_record:
                current_record.delta_counters_a = 0
                current_record.delta_counters_b = 0
            record.save_changed_fields(current_record=current_record, new_record=record, notify_transport_enabled=False,
                                       save_to_graph=False)
            # commit() # not needed?

            # enable below only for testing on netbook
            # if Constant.HOST_NAME == 'xxxnetbook' and (record.delta_counters_a or record.delta_counters_b):
            #    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=owsensor_loop.sampling_period_seconds)
    except Exception as ex:
        L.l.error('Error on sensor update, err {}'.format(ex), exc_info=True)
        db.session.rollback()
Beispiel #4
0
def rule_sensor_temp_extreme(obj=models.Sensor(), field_changed_list=None):
    if hasattr(obj, 'temperature') and obj.temperature is not None:
        m = models.ZoneSensor
        zonesensor = m().query_filter_first(m.sensor_name == obj.sensor_name)
        if zonesensor is not None and zonesensor.target_material is not None:
            m = models.Zone
            zone = m().query_filter_first(m.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 max_temp.has_key(zonesensor.target_material):
                    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 min_temp.has_key(zonesensor.target_material):
                    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
Beispiel #5
0
def rule_openhab_sensor(obj=models.Sensor(), field_changed_list=None):
    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)
Beispiel #6
0
def thread_run():
    if not P.initialised:
        init_solar_aps()
    if P.initialised:
        try:
            production = utils.parse_http(
                model_helper.get_param(Constant.P_SOLAR_APS_LOCAL_URL),
                P.start_keyword, P.end_keyword)
            last_power = utils.parse_http(
                model_helper.get_param(Constant.P_SOLAR_APS_LOCAL_URL),
                P.start_keyword_now, P.end_keyword_now)
            temperature = utils.parse_http(model_helper.get_param(
                Constant.P_SOLAR_APS_LOCAL_REALTIME_URL),
                                           P.start_key_temp,
                                           P.end_key_temp,
                                           end_first=True)
            panel_id = utils.parse_http(model_helper.get_param(
                Constant.P_SOLAR_APS_LOCAL_REALTIME_URL),
                                        P.start_key_panel,
                                        P.end_key_panel,
                                        end_first=True)
            utility_name = model_helper.get_param(
                Constant.P_SOLAR_UTILITY_NAME)
            if temperature is not None:
                zone_sensor = models.ZoneSensor.query.filter_by(
                    sensor_address=panel_id).first()
                if zone_sensor is None:
                    L.l.warning(
                        'Solar panel id {} is not defined in zone sensor list'.
                        format(panel_id))
                record = models.Sensor(address=panel_id)
                current_record = models.Sensor.query.filter_by(
                    address=panel_id).first()
                record.type = 'solar'
                if current_record is not None:
                    record.sensor_name = current_record.sensor_name
                else:
                    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(current_record=current_record,
                                           new_record=record,
                                           notify_transport_enabled=True,
                                           save_to_graph=True,
                                           debug=False)
            if production is not None:
                production = float(production)
                record = models.Utility()
                record.utility_name = utility_name
                current_record = models.Utility.query.filter_by(
                    utility_name=utility_name).first()
                if current_record is not None:
                    if current_record.units_total is None:
                        record.units_delta = 0
                    else:
                        record.units_delta = production - current_record.units_total
                        if record.units_delta == 0:
                            # do not waste db space if no power generated
                            return
                    record.units_total = production
                    record.unit_name = current_record.unit_name
                    record.units_2_delta = last_power
                    record.unit_2_name = current_record.unit_2_name
                else:
                    record.units_delta = production
                    record.units_total = production
                if current_record.unit_cost is None:
                    current_record.unit_cost = 0.0
                record.cost = 1.0 * record.units_delta * current_record.unit_cost
                record.save_changed_fields(current_record=current_record,
                                           new_record=record,
                                           debug=False,
                                           notify_transport_enabled=True,
                                           save_to_graph=True,
                                           save_all_fields=False)
        except Exception as ex:
            L.l.warning("Got exception on solar thread run, ex={}".format(ex))
Beispiel #7
0
def mqtt_on_message(client, userdata, 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:
            sensor_name = msg.topic.split(topic_clean)[1].split('/')[1]
            obj = utils.json2obj(msg.payload)
            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
                # 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 = models.ZoneSensor.query.filter_by(sensor_address=sensor_name).first()
                    if zone_sensor is not None:
                        current_record = models.Sensor.query.filter_by(address=sensor_name).first()
                        if current_record is None:
                            pass
                        else:
                            current_record.vad = None
                            current_record.iad = None
                            current_record.vdd = None
                        record = models.Sensor(address=sensor_name, sensor_name=zone_sensor.sensor_name)
                        record.is_event_external = True
                        if voltage is not None:
                            record.vad = round(voltage, 0)
                            record.save_changed_fields(current_record=current_record, new_record=record,
                                                       notify_transport_enabled=True, save_to_graph=True, debug=False)
                        if current is not None:
                            record.iad = round(current, 1)
                            record.save_changed_fields(current_record=current_record, new_record=record,
                                                       notify_transport_enabled=True, save_to_graph=True, debug=False)
                        if factor is not None:
                            record.vdd = round(factor, 1)
                            record.save_changed_fields(current_record=current_record, new_record=record,
                                                       notify_transport_enabled=True, save_to_graph=True, debug=False)
                # dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=current, unit='kWh')
            elif 'POWER' in obj:
                power_is_on = obj['POWER'] == 'ON'
                current_relay = models.ZoneCustomRelay.query.filter_by(gpio_pin_code=sensor_name,
                                                                       gpio_host_name=Constant.HOST_NAME).first()
                if current_relay is not None:
                    L.l.info("Got relay {} state={}".format(sensor_name, power_is_on))
                    new_relay = models.ZoneCustomRelay(gpio_pin_code=sensor_name, gpio_host_name=Constant.HOST_NAME)
                    new_relay.relay_is_on = power_is_on
                    current_relay.is_event_external = True
                    models.ZoneCustomRelay().save_changed_fields(current_record=current_relay, new_record=new_relay,
                                                                 notify_transport_enabled=True, save_to_graph=True)
                else:
                    L.l.error("ZoneCustomRelay with code {} does not exist in database".format(sensor_name))
            elif 'COUNTER' in obj:
                # TelePeriod 60
                counter = obj['COUNTER']
                for i in [1, 2, 3, 4]:
                    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)
            elif 'BMP280' in obj:
                # iot/sonoff/tele/sonoff-basic-3/SENSOR =
                # {"Time":"2018-10-28T08:12:26","BMP280":{"Temperature":24.6,"Pressure":971.0},"TempUnit":"C"}
                bmp = obj['BMP280']
                temp = bmp['Temperature']
                press = bmp['Pressure']
                sensor_address = '{}_{}'.format(sensor_name, 'bmp280')
                current_zone_sensor = models.ZoneSensor.query.filter_by(sensor_address=sensor_address).first()
                if current_zone_sensor is not None:
                    current_sensor = models.Sensor.query.filter_by(address=sensor_address).first()
                    sensor = models.Sensor(address=sensor_address, sensor_name=current_zone_sensor.sensor_name)
                    sensor.temperature = temp
                    sensor.pressure = press
                    sensor.save_changed_fields(
                        current_record=current_sensor, notify_transport_enabled=True, save_to_graph=True)
                else:
                    L.l.info('Undefined sensor found in {}, value={}'.format(sensor_address, bmp))
            else:
                L.l.warning("Usefull payload missing from topic {} payload={}".format(msg.topic, msg.payload))
        else:
            L.l.warning("Invalid sensor topic {}".format(msg.topic))
Beispiel #8
0
def save_to_db(dev):
    # global db_lock
    # db_lock.acquire()
    try:
        delta_time_counters = None
        address = dev['address']
        record = models.Sensor(address=address)
        assert isinstance(record, models.Sensor)
        zone_sensor = models.ZoneSensor.query.filter_by(
            sensor_address=address).first()
        current_record = models.Sensor.query.filter_by(address=address).first()
        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()
        if dev.has_key('counters_a'):
            record.counters_a = dev['counters_a']
            if current_record:
                record.delta_counters_a = record.counters_a - current_record.counters_a
                # get accurate interval (e.g. to establish power consumption in watts)
                delta_time_counters = (
                    utils.get_base_location_now_date() -
                    current_record.updated_on).total_seconds()
            else:  # when running first time with db empty
                record.delta_counters_a = 0  # don't know prev. count, assume no consumption (ticks could be lost)
                delta_time_counters = P.sampling_period_seconds
        if dev.has_key('counters_b'):
            record.counters_b = dev['counters_b']
            if current_record:
                record.delta_counters_b = record.counters_b - current_record.counters_b
            else:
                # fixme: don't know prev. count, assume no consumption (ticks could be lost)
                record.delta_counters_b = 0
        if dev.has_key('temperature'):
            record.temperature = dev['temperature']
        if dev.has_key('humidity'):
            record.humidity = dev['humidity']
        if dev.has_key('iad'):
            record.iad = dev['iad']
        if dev.has_key('vad'):
            record.vad = dev['vad']
        if dev.has_key('vdd'):
            record.vdd = dev['vdd']
        if dev.has_key('pio_a'):
            record.pio_a = dev['pio_a']
        if dev.has_key('pio_b'):
            record.pio_b = dev['pio_b']
        if dev.has_key('sensed_a'):
            record.sensed_a = dev['sensed_a']
        if dev.has_key('sensed_b'):
            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
        if current_record is not None:
            if record.delta_counters_a != 0:
                current_record.delta_counters_a = 0
            if record.delta_counters_b != 0:
                current_record.delta_counters_b = 0
        record.save_changed_fields(current_record=current_record,
                                   new_record=record,
                                   notify_transport_enabled=True,
                                   save_to_graph=True,
                                   debug=False)
        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)
Beispiel #9
0
def set_main_heat_source():
    heat_source_relay_list = models.ZoneHeatRelay.query.filter(
        models.ZoneHeatRelay.temp_sensor_name is not None).all()
    up_limit = P.temp_limit + P.threshold
    for heat_source_relay in heat_source_relay_list:
        # is there is a temp sensor defined, consider this source as possible alternate source
        if heat_source_relay.temp_sensor_name is not None:
            temp_rec = models.Sensor().query_filter_first(
                models.Sensor.sensor_name.in_(
                    [heat_source_relay.temp_sensor_name]))
            # if alternate source is valid
            # fixok: add temp threshold to avoid quick on/offs
            if temp_rec is not None \
                    and ((temp_rec.temperature >= up_limit and not heat_source_relay.is_alternate_source_switch)
                         or (temp_rec.temperature >= P.temp_limit and heat_source_relay.is_alternate_source_switch)):
                if heat_source_relay.is_alternate_source_switch:
                    # stop main heat source
                    heatrelay_main_source = models.ZoneHeatRelay.query.filter_by(
                        is_main_heat_source=1).first()
                    main_source_zone = models.Zone.query.filter_by(
                        id=heatrelay_main_source.zone_id).first()
                    __save_heat_state_db(zone=main_source_zone,
                                         heat_is_on=False)
                    # turn switch valve on to alternate position
                    switch_source_zone = models.Zone.query.filter_by(
                        id=heat_source_relay.zone_id).first()
                    __save_heat_state_db(zone=switch_source_zone,
                                         heat_is_on=True)
                else:
                    # mark this source as active, to be started when there is heat need
                    if heat_source_relay.is_alternate_heat_source is False:
                        L.l.info(
                            'Alternate heat source is active with temp={}'.
                            format(temp_rec.temperature))
                    heat_source_relay.is_alternate_heat_source = True
                commit()
            else:
                # if alternate source is no longer valid
                if heat_source_relay.is_alternate_source_switch:
                    # stop alternate heat source
                    #heatrelay_alt_source = models.ZoneHeatRelay.query.filter_by(is_alternate_heat_source=1).first()
                    #if heatrelay_alt_source is not None:
                    #    alt_source_zone = models.Zone.query.filter_by(id=heatrelay_alt_source.zone_id).first()
                    #    __save_heat_state_db(zone=alt_source_zone, heat_is_on=False)
                    # turn valve back to main position
                    switch_source_zone = models.Zone.query.filter_by(
                        id=heat_source_relay.zone_id).first()
                    __save_heat_state_db(zone=switch_source_zone,
                                         heat_is_on=False)
                else:
                    # mark this source as inactive, let main source to start
                    if heat_source_relay.is_alternate_heat_source:
                        # force alt source shutdown if was on
                        alt_source_zone = models.Zone.query.filter_by(
                            id=heat_source_relay.zone_id).first()
                        __save_heat_state_db(zone=alt_source_zone,
                                             heat_is_on=False)
                        # todo: sleep needed to allow for valve return
                    if heat_source_relay.is_alternate_heat_source is True:
                        L.l.info(
                            'Alternate heat source is now inactive, temp source is {}'
                            .format(temp_rec))
                    heat_source_relay.is_alternate_heat_source = False
                commit()
Beispiel #10
0
def rule_sensor_temp_target(obj=models.Sensor(), field_changed_list=None):
    if not field_changed_list:
        field_changed_list = []
    #temp = obj.temperature
    return 'rule temp ok'