Пример #1
0
def update_device(jdevice):
    dprint('updating: ', jdevice)
    device_id = jdevice.get('device_id') or None
    if (not device_id or len(device_id) == 0):
        return ({'error': 'Invalid device_id'})
    dev = Device.query.filter_by(device_id=device_id).first()
    if not dev:
        return ({'error': 'device_id does not exist'})
    fallback_id = jdevice.get('fallback_id') or None
    # fallback_id must be a unique non-empty string
    if (fallback_id and len(fallback_id) > 0):
        fb = Device.query.filter_by(fallback_id=fallback_id).first()
        if fb:
            return ({'error': 'fallback_id already exists'})
        dev.fallback_id = fallback_id
    mac = jdevice.get('mac') or None
    if (mac): dev.mac = mac
    ip = jdevice.get('ip') or None
    if (ip): dev.ip = ip
    hardware_type = jdevice.get('hardware_type') or None
    if (hardware_type): dev.hardware_type = hardware_type
    num_relays = jdevice.get('num_relays') or -1
    if (num_relays >= 0): dev.num_relays = num_relays
    num_sensors = jdevice.get('num_sensors') or -1
    if (num_sensors >= 0): dev.num_sensors = num_sensors
    print(jdevice.get('enabled'))
    ####enab = jdevice.get('enabled') or None   # TODO: understand this!
    if 'enabled' in jdevice:
        dev.enabled = jdevice.get('enabled')
    db.session.commit()
    return ({'result': 'successfully updated device'})
Пример #2
0
def update_device(jdevice):
    dprint('updating: ', jdevice)
    device_id = jdevice.get('device_id') or None
    if (not device_id or len(device_id) == 0):
        return ({'result': False, 'error': 'Invalid device_id'})
    dev = Device.query.filter_by(device_id=device_id).first()
    if not dev:
        return ({'result': False, 'error': 'device_id does not exist'})
    # TODO: keep the default fallback id of Tasmota as it is; do not change it!
    fallback_id = jdevice.get('fallback_id') or None
    # fallback_id must be a unique non-empty string
    if (fallback_id and len(fallback_id) > 0):
        fb = Device.query.filter_by(fallback_id=fallback_id).first()
        if fb:
            return ({'result': False, 'error': 'fallback_id already exists'})
        dev.fallback_id = fallback_id
    # TODO: keep the discovered MAC and IP of the device without changing
    mac = jdevice.get('mac') or None
    if (mac): dev.mac = mac
    ip = jdevice.get('ip') or None
    if (ip): dev.ip = ip
    hardware_type = jdevice.get('hardware_type') or None
    if (hardware_type): dev.hardware_type = hardware_type
    # TODO: auto discover num_relays
    num_relays = jdevice.get('num_relays') or -1
    if (num_relays >= 0): dev.num_relays = num_relays
    # TODO: there is only one sensor object containing a JSON of all sensors
    num_sensors = jdevice.get('num_sensors') or -1
    if (num_sensors >= 0): dev.num_sensors = num_sensors
    print(jdevice.get('enabled'))
    ####enab = jdevice.get('enabled') or None   # TODO: understand the if(None) behaviour of a boolean variable in python!
    if 'enabled' in jdevice:
        dev.enabled = jdevice.get('enabled')
    db.session.commit()
    return ({'result': True, 'msg': 'successfully updated device'})
Пример #3
0
def ping_relsens(device_id):
    if SIMULATION_MODE:
        dprint('In simulation mode: not pinging the relays')
        return
    dprint('\nPinging relsens in the device: ', device_id)
    topic = '{}/{}/{}'.format(PUB_PREFIX, device_id, BROADCAST_RELSEN)
    mqtt.publish(topic, EMPTY_PAYLOAD)
Пример #4
0
def create_test_db():
    dprint('Deleting the old database...')
    db.drop_all()  # to avoid violating the unique value constraints
    dprint('Creating a test database...')
    db.create_all()
    #return (add_test_data())
    return ({'result': 'Hub DB created'})
Пример #5
0
def insert_device(device_id,
                  fallback_id=None,
                  mac=None,
                  ip=None,
                  hardware_type="Generic",
                  num_relays=1,
                  num_sensors=0,
                  enabled=True):
    if (not device_id or len(device_id) == 0):
        dprint('Invalid device_id')
        return False  # TODO: return the error reason also
    dprint('inserting device id: {}'.format(device_id))
    # check for existing device (device_id must be unique)
    dev = Device.query.filter_by(device_id=device_id).first()
    if dev:
        dprint('Device ID already exists: {}'.format(device_id))
        return False
    # check for existing device (fallback_id must be unique)
    if (fallback_id):
        fb = Device.query.filter_by(fallback_id=fallback_id).first()
        if fb:
            dprint('Falback ID already exists: {}'.format(fallback_id))
            return False
    dev = Device(device_id=device_id,
                 fallback_id=fallback_id,
                 mac=mac,
                 ip=ip,
                 hardware_type=hardware_type,
                 num_relays=num_relays,
                 num_sensors=num_sensors,
                 enabled=enabled)
    db.session.add(dev)
    db.session.commit()
    dprint('Added device: {}'.format(dev))
    return True
Пример #6
0
def update_timer(device_id, relsen_id, timer_list, repeat=True):
    dprint('updating timer for {}/{}..'.format(device_id, relsen_id))
    relay = 1
    if (relsen_id == 'POWER'):  # the device has a single relay
        relay = 1  # redundant, but for clarity
    else:
        relay = relsen_id[
            -1]  # ASSUMPTION: relsen id is in the form POWER1, POWER2 etc. ***
        if relay < '0' or relay > '4':
            print('relsen_id has to be from POWER1 to POWER4 only')
            return Flase
    relay_num = int(relay)  # relay number 1 to 4 within the device
    if len(timer_list) > 2:
        print(
            '\n**** CAUTION: Only 2 sechedules allowed per relay; others are ignored ****\n'
        )

    if SIMULATION_MODE:
        dprint('In Simulation Mode: not setting timer')
        return True

    starting_timer_id = (relay_num - 1) * 4 + 1
    send_timer_command(device_id, relay_num, starting_timer_id, timer_list[0],
                       repeat)  # this sets up 2 timers: one ON, and one OFf
    if len(timer_list) > 1:  # process the second pair of schedule times
        send_timer_command(device_id, relay_num, starting_timer_id + 2,
                           timer_list[1], repeat)  # setup the next 2 timers
    return True
Пример #7
0
def send_timer_command(device_id, relay_num, timer_id, time_pair, repeat):
    suffix = 'Timer' + str(timer_id)  # timer_id can be from 1 to 16
    topic = '{}/{}/{}'.format(PUB_PREFIX, device_id, suffix)
    pl = {
        "Enable": 1,
        "Mode": 0,
        "Time": time_pair[0],
        "Window": 0,
        "Days": "1111111",
        "Repeat": repeat,
        "Output": relay_num,
        "Action": 1
    }  # ON
    payload = json.dumps(pl)
    dprint(topic, payload)
    mqtt.publish(topic, payload)
    # OFF timer for the same relay:
    suffix = 'Timer' + str(timer_id + 1)
    topic = '{}/{}/{}'.format(PUB_PREFIX, device_id, suffix)
    pl = {
        "Enable": 1,
        "Mode": 0,
        "Time": time_pair[1],
        "Window": 0,
        "Days": "1111111",
        "Repeat": repeat,
        "Output": relay_num,
        "Action": 0
    }  # OFF
    payload = json.dumps(pl)
    dprint(topic, payload)
    mqtt.publish(topic, payload)
Пример #8
0
def update_sensor_reading (device_id, str_msg):  # TODO: save it in in-memory status (and later, in the database)
    #try:
    dprint ('sensor reading for: ', device_id)
    dprint (str_msg)
    jsensor = json.loads(str_msg)
    in_mem_status[devid][SENSOR_RELSEN] = jsensor['StatusSNS'] # this is stored as an inner json
    jstatus = {'device_id' : device_id, 'relsen_id' : SENSOR_RELSEN, 'status' : jsensor['StatusSNS']}
    socketio.emit (SERVER_EVENT, jstatus)
Пример #9
0
def ping_mqtt():
    if SIMULATION_MODE:
        dprint ('In simulation mode: not pinging MQTT devices')
        return
    dprint ('\nPinging all devices...')
    topic = '{}/{}/{}'.format (PUB_PREFIX, BROADCAST_DEVICE, PUB_SUFFIX) # POWER
    #dprint (topic, ' (blank)')
    mqtt.publish (topic, EMPTY_PAYLOAD)    
Пример #10
0
def send_tracer_broadcast():
    if SIMULATION_MODE:
        dprint('In simulation mode: not sending tracer')
        return
    topic = '{}/{}/{}'.format(PUB_PREFIX, BROADCAST_DEVICE,
                              BROADCAST_RELSEN)  # POWER0
    dprint('Sending probe to: ', topic)
    mqtt.publish(topic, EMPTY_PAYLOAD)  # empty payload gets the relay status
Пример #11
0
def remove_all_relsens(current_user):
    dprint('{} is removing all relays/sensors...'.format(current_user))
    rel = Relsen.query.all()
    dprint('{} records found.'.format(len(rel)))
    for rs in rel:
        db.session.delete(rs)
    db.session.commit()
    return ({'result': 'All relays & sensors removed.'})
Пример #12
0
def remove_all_status(current_user):
    dprint('{} is removing all status data...'.format(current_user))
    sta = Status.query.all()
    dprint('{} records found.'.format(len(sta)))
    for s in sta:
        db.session.delete(s)
    db.session.commit()
    return ({'result': 'All status data removed.'})
Пример #13
0
def onboard_device(jnew_device):
    dprint('\nOnboarding: ', jnew_device)
    if not insert_device(jnew_device['device_id']):
        return False
    for rsid in jnew_device['relsen_list']:
        if not insert_relsen(jnew_device['device_id'], rsid):
            return False
    build_device_inventory()  # update in-mem structures
    return True
Пример #14
0
def start_daemon():
    if SIMULATION_MODE:
        dprint('\n* In Simulation Mode: not starting daemon thread *\n')
        return
    global bgthread
    print('\nChecking daemon...')
    with thread_lock:
        if bgthread is None:  # as this should run only once
            print('\nStarting background thread...\n')
            bgthread = socketio.start_background_task(bgtask)
Пример #15
0
def onboard_device(jnew_device):
    dprint('\nOnboarding: ', jnew_device)
    if not insert_device(jnew_device['device_id']):
        return False
    for rsid in jnew_device['relsen_list']:
        if not insert_relsen(jnew_device['device_id'], rsid):
            return False
    ###build_device_inventory()  # update in-mem structures; TODO: revisit this. needs testing. will this call succeed, since they are disabled?
    build_active_device_inventory_route()  # this initializes their status also
    return True
Пример #16
0
def send_tracer_broadcast():  
    global sensor_count
    if SIMULATION_MODE:
        dprint ('In simulation mode: not sending tracer')
        return
    topic = '{}/{}/{}'.format (PUB_PREFIX, BROADCAST_DEVICE, BROADCAST_RELSEN) # POWER0
    dprint ('Sending probe to: ',topic)
    mqtt.publish (topic, EMPTY_PAYLOAD)  # empty payload gets the relay status
    sensor_count = (sensor_count+1) % SENSOR_INTERVAL   
    if (sensor_count==0):
        request_sensor_reading(BROADCAST_DEVICE)
Пример #17
0
def update_network_params (device_id, str_msg):  # TODO: save it in in-memory cache and database   
    global in_mem_network 
    print ('Network settings for: ', device_id)
    netparams = json.loads(str_msg)
    if (device_id not in in_mem_network):
        in_mem_network[device_id] = {}
    in_mem_network[device_id]['host_name'] = netparams['StatusNET']['Hostname']
    in_mem_network[device_id]['ip_address'] =  netparams['StatusNET']['IPAddress'] 
    in_mem_network[device_id]['mac_id'] =  netparams['StatusNET']['Mac']  
    #dprint (netparams)
    dprint(in_mem_network[device_id])
Пример #18
0
def subscribe_mqtt():
    global subscribed
    if SIMULATION_MODE:  # TODO: implement such a wrapper method for mqtt.publish()  also
        dprint('\n* In Simulation Mode: not subscribing to MQTT *\n')
        return
    # TODO: additional subscriptions like TELE
    # do not check the 'subscribed' flag here: this may be a reconnect event!
    print('Subscribing to MQTT: %s' % (SUB_TOPIC))
    mqtt.subscribe(SUB_TOPIC)  # duplicate subscriptions are OK
    print('Subscribing to MQTT: %s' % (LWT_TOPIC))
    mqtt.subscribe(LWT_TOPIC)  # duplicate subscriptions are OK
    subscribed = True  # tell socketIO.on_connect() not to subscribe again
Пример #19
0
def start_daemon():
    global bgthread, TERMINATE
    if SIMULATION_MODE:
        dprint('\n* In Simulation Mode: not starting daemon thread *\n')
        return
    print('\nChecking daemon...')
    with thread_lock:
        if bgthread is None:  # as this should run only once
            print('\nStarting background thread...\n')
            TERMINATE = False  # reset the flag -it it was earlier stopped manually
            bgthread = socketio.start_background_task(bgtask)
    return {'result': True, 'msg': 'Worker thread started.'}
Пример #20
0
def get_devices_in_room():
    room = request.args.get('room_name')
    if (not room):
        return ({'error': 'room_name is required'})
    relsens = Relsen.query.filter_by(room_name=room).all()
    devices = set([])  # to avoid duplicates
    for rs in relsens:
        devices.add(rs.controller)
    dprint(devices)
    dprint(type(devices))
    retval = []
    for d in devices:
        retval.append(d.toJSON())
    return ({'devices': retval})
Пример #21
0
def insert_relsen(device_id,
                  relsen_id,
                  relsen_name=None,
                  relsen_type=None,
                  room_name=None,
                  room_type=None,
                  group_name=None,
                  schedule=None,
                  repeat=False):
    dprint('inserting relsen: {}:{}'.format(device_id, relsen_id))
    # check for existance of device (device_id must preexist)
    dev = Device.query.filter_by(device_id=device_id).first()
    if not dev:
        dprint('Unknown device ID: {}'.format(device_id))
        return False
    for rel in dev.relsens:  # combination of (device_id+relsen_id) must be unique
        if (rel.relsen_id == relsen_id):
            dprint('{}.{} already exists'.format(device_id, relsen_id))
            return False
    rs = Relsen(
        controller=dev,
        relsen_id=relsen_id,
        relsen_name=relsen_name,
        relsen_type=relsen_type,
        room_name=room_name,
        room_type=room_type,
        group_name=group_name,
        schedule=
        schedule,  # LHS is the DB column name; RHS is a stringified json object (which has 'schedule' as the key)
        repeat=repeat)
    db.session.add(rs)
    db.session.commit()
    dprint('Added relay: {}'.format(rs))
    return True
Пример #22
0
def remove_device(current_user):
    devid = request.args.get('device_id')
    if (not devid):
        return ({'error': 'device_id is required'})
    dev = Device.query.filter_by(device_id=devid).first()
    if (not dev):
        return ({'error': 'invalid device_id'})
    dprint('{} is removing a device record..'.format(current_user))
    for rs in dev.relsens:
        db.session.delete(rs)
    for sta in dev.stat:
        db.session.delete(sta)
    db.session.delete(dev)
    db.session.commit()
    return ({'result': 'Device record removed.'})
Пример #23
0
def remove_all_devices(current_user):
    dprint(remove_all_status())  # this is needed for dependency constraint
    dprint(remove_all_relsens())
    dprint('{} is removing all device records...'.format(current_user))
    devs = Device.query.all()
    dprint('{} records found.'.format(len(devs)))
    for d in devs:
        db.session.delete(d)
    db.session.commit()
    return ({'result': 'All device records removed.'})
Пример #24
0
def bgtask():
    dprint ('Entering background thread...')
    global TERMINATE
    while not TERMINATE:
        socketio.sleep (PING_INTERVAL) # stop/daemon will be mostly called during this sleep
        if TERMINATE : 
            break
        #dprint ('\nWaking !...')
        for devid in is_online:
            if (not is_online[devid]['online']):  # TODO: Make 3 attempts before declaring it offline
                is_online[devid]['count'] = (is_online[devid]['count']+1) % MAX_RETRIES
                if (is_online[devid]['count']==0):
                    mark_offline (devid)
                    send_offline_notification (devid)
        for devid in is_online:
            is_online[devid]['online'] = False    # reset for next round of checking   
        send_tracer_broadcast() # get status of all relays: Necessary, when a device comes out of the offline mode
    print ('\n *** Background thread terminates. ***\n')  
Пример #25
0
def insert_status (device_id, relay_status=None, sensor_values=None,    # time_stamp=None, 
                event_type=None, online=True): 
    return {'result' : 'this is a placeholder'}
    
    # check for existance of device (device_id must already exist)
    dev = Device.query.filter_by (device_id=device_id).first() 
    if not dev: 
        dprint ('Unknown device ID: {}'.format(device_id))
        return False
    st = Status ( 
        controller = dev,
        relay_status = relay_status,  
        sensor_values = sensor_values,
        event_type = event_type,
        online = online) 
    db.session.add (st) 
    db.session.commit()   
    dprint ('Added status: {}'.format(st))
    return True   
Пример #26
0
def clear_timers (device_id, relsen_id):
    dprint ('Clearing all timers for: {}/{}'.format (device_id, relsen_id))
    relay = 1
    if (relsen_id == 'POWER'):      # the device has a single relay 
        relay = 1                   # redundant, but for clarity
    else:
        relay = relsen_id[-1]           # ASSUMPTION: relsen id is in the form POWER1, POWER2 etc. ***    
        if relay < '0' or relay > '4':  
            print ('relsen_id has to be from POWER1 to POWER4 only')
            return False
    relay_num = int(relay)          # relay number 1 to 4 within the device
    starting_timer_id = (relay_num-1)*4 + 1    
    for i in range (0, MAX_TIMERS):
        relsen = 'Timer'+ str(starting_timer_id+i)
        topic = '{}/{}/{}'.format (PUB_PREFIX, device_id, relsen)   
        payload = json.dumps({"Enable":0})
        dprint (topic, payload)
        mqtt.publish (topic, payload)
    return True
Пример #27
0
def ping_device(device_id):
    if SIMULATION_MODE:
        dprint('In simulation mode: not pinging the device')
        return
    dprint('\nPinging the device: ', device_id)
    topic = '{}/{}/{}'.format(PUB_PREFIX, device_id, PUB_SUFFIX)  # POWER
    dprint(topic, ' (blank)')
    mqtt.publish(topic, EMPTY_PAYLOAD)
Пример #28
0
def update_relsen(jrelsen):
    device_id = jrelsen.get('device_id') or None
    if (not device_id or len(device_id) == 0):
        return ({'error': 'Invalid device_id'})
    relsen_id = jrelsen.get('relsen_id') or None
    if (not relsen_id or len(relsen_id) == 0):
        return ({'error': 'Invalid relsen_id'})
    dev = Device.query.filter_by(device_id=device_id).first()
    if not dev:
        return ({'error': 'device_id does not exist'})
    rs = Relsen.query.filter_by(device_id=device_id,
                                relsen_id=relsen_id).first()
    if (not rs):
        return ({'error': 'relsen_id does not exist'})
    relsen_name = jrelsen.get('relsen_name') or None
    if (relsen_name): rs.relsen_name = relsen_name
    relsen_type = jrelsen.get('relsen_type') or None
    if (relsen_type): rs.relsen_type = relsen_type
    room_name = jrelsen.get('room_name') or None
    if (room_name): rs.room_name = room_name
    room_type = jrelsen.get('room_type') or None
    if (room_type): rs.room_type = room_type
    group_name = jrelsen.get('group_name') or None
    if (group_name): rs.group_name = group_name
    dprint('incoming schedule: ', jrelsen.get('schedule'))
    schedule = jrelsen.get('schedule')
    if (schedule is None):  # an empty array evaluates to False ***
        schedule = []
    dprint('new schedule: ', schedule)
    dprint('updating schedule..')
    rs.schedule = json.dumps(
        {'schedule': schedule}
    )  # store it as a stringified json (so, the 'schedule' key is again needed!)
    rs.repeat = False
    if 'repeat' in jrelsen:
        rs.repeat = jrelsen.get('repeat')
    db.session.commit()
    enable_device(
        device_id
    )  # every time you configure a device, mark it enabled; this is useful for new devices that are onboarded in a disabled state ***
    if (schedule is not None and len(schedule) > 0):
        dprint('sending schedule update to Tasmota..')
        update_timer(device_id, relsen_id, jrelsen.get('schedule'), rs.repeat)
    return ({'result': 'successfully updated relsen'})
Пример #29
0
def get_latest_db_status():
    dprint('Status class not implemented')
    return ({
        'place_holder': 'not implemented'
    })  # TODO: this is to get the last known status from the
Пример #30
0
def delete_test_db(current_user):
    dprint('{} is deleting the Hub database...'.format(current_user))
    db.drop_all()
    return ({'result': 'Test DB removed'})