Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add device settings to UI #25

Merged
merged 17 commits into from
Nov 10, 2023
7 changes: 6 additions & 1 deletion init_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy_json import mutable_json_type
from modules.helpers import logger, generateToken
from modules.domoticz import saveJson

app = Flask(__name__)

Expand All @@ -23,6 +24,7 @@ class User(db.Model):
admin = db.Column(db.Boolean, default=False, nullable=False)
googleassistant = db.Column(db.Boolean, default=False, nullable=False)
authtoken = db.Column(db.String(100))
device_config = db.Column(mutable_json_type(dbtype=db.JSON, nested=True))

def __repr__(self):
return f"<User {self.id}>"
Expand Down Expand Up @@ -58,8 +60,9 @@ def __repr__(self):
domouser='domoticz',
domopass='password',
admin=True,
googleassistant=True,
googleassistant=False,
authtoken=generateToken(username),
device_config={}
)
db.session.add(new_user)

Expand Down Expand Up @@ -90,6 +93,8 @@ def __repr__(self):
db.session.add(settings)
try:
db.session.commit()
data = {}
saveJson(username, data)
logger.info("Database is created...")
except Exception:
logger.info('Database already created')
3 changes: 2 additions & 1 deletion modules/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class User(UserMixin, db.Model):
admin = db.Column(db.Boolean, default=False, nullable=False)
googleassistant = db.Column(db.Boolean, default=False, nullable=False)
authtoken = db.Column(db.String(100))
device_config = db.Column(mutable_json_type(dbtype=db.JSON, nested=True))

def __repr__(self):
return f"<User {self.id}>"
Expand All @@ -37,4 +38,4 @@ class Settings(db.Model):
armlevels = db.Column(mutable_json_type(dbtype=db.JSON, nested=True))

def __repr__(self):
return f"<Settings {self.id}>"
return f"<Settings {self.id}>"
30 changes: 26 additions & 4 deletions modules/domoticz.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ def AogGetDomain(device):
return None
return None


def getDesc(user_id, state):
user = User.query.filter_by(username=user_id).first()

if state.id in user.device_config:
desc = user.device_config[state.id]
return desc
else:
return None


# Get additional settings from domoticz description in <voicecontrol> </voicecontrol>tags
def getDeviceConfig(descstr):
ISLIST = ['nicknames']
Expand Down Expand Up @@ -60,6 +71,7 @@ def getDeviceConfig(descstr):
except:
logger.error('Error parsing device configuration from Domoticz device description:', rawconfig[0])
return None

return cfgdict
return None

Expand All @@ -80,7 +92,7 @@ def getAog(device, user_id=None):
aog.id = domain + "_" + device.get('idx')
aog.name = {
'name' : device.get('Name').replace(" ","_"),
'nicknames': [device.get('Name')]
'nicknames': []
}
if device.get('Type') in ['Light/Switch', 'Color Switch', 'Lighting 1', 'Lighting 2', 'Lighting 5', 'RFY', 'Value']:
aog.type = 'action.devices.types.LIGHT'
Expand All @@ -92,6 +104,7 @@ def getAog(device, user_id=None):
aog.type = 'action.devices.types.FAN'
if device.get('Image') == 'Heating':
aog.type = 'action.devices.types.HEATER'
aog.customData['dzTags'] = False

""" Get additional settings from domoticz description
<voicecontrol>
Expand All @@ -108,7 +121,16 @@ def getAog(device, user_id=None):
(supports levelnames: 'off', 'heat', 'cool', 'auto', 'fan-only', 'purifier', 'eco', 'dry')
</voicecontrol>
"""
desc = getDeviceConfig(device.get("Description"))

# Try to get device specific voice control configuration from Domoticz
desc = getDesc(user_id, aog)
if desc is None:
desc = getDeviceConfig(device.get("Description"))
if desc is not None:
logger.info('<voicecontrol> tags found for idx %s in domoticz description.', aog.id)
aog.customData['dzTags'] = True


if desc is not None:
n = desc.get('nicknames', None)
if n is not None:
Expand Down Expand Up @@ -152,7 +174,7 @@ def getAog(device, user_id=None):
aog.customData['idx'] = device.get('idx')
aog.customData['domain'] = domain
aog.customData['protected'] = device.get('Protected')
aog.notificationSupportedByAgent = (True if domain in ['SmokeDetector', 'Doorbell'] else False)
aog.notificationSupportedByAgent = (True if domain in ['SmokeDetector', 'Doorbell', 'DoorLock'] else False)

if domain == 'Scene':
aog.type = 'action.devices.types.SCENE'
Expand Down Expand Up @@ -353,7 +375,7 @@ def queryDomoticz(username, url):
auth=domoCredits, timeout=5.00)
except:
return "{}"

return r.text

def getDomoticzDevices(user_id):
Expand Down
139 changes: 125 additions & 14 deletions modules/routes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import modules.config as config
import json
from time import sleep

from flask_login import login_required, current_user
Expand Down Expand Up @@ -36,17 +37,131 @@ def dashboard():

@login_required
def devices():

reportstate = report_state.enable_report_state()
dbsettings = Settings.query.get_or_404(1)
devices = get_devices(current_user.username)
dbuser = User.query.filter_by(username=current_user.username).first()

if request.method == "POST":

deviceconfig = dbuser.device_config

if request.form['submit'] == 'device_settings':
idx = request.form.get('device_id')
hideDevice = request.form.get('hideDevice')
willReportState = request.form.get('willReportState')
challenge = request.form.get('2FA')
room = request.form.get('room')
nicknames = request.form.get('nicknames')
devicetype = request.form.get('devicetype')
minThreehold = request.form.get('minThreehold')
maxThreehold = request.form.get('maxThreehold')
camurl = request.form.get('camurl')
actual_temp_idx = request.form.get('actual_temp_idx')
selector_modes_idx = request.form.get('selector_modes_idx')

if idx not in deviceconfig.keys():
deviceconfig[idx] = {}

if hideDevice == 'on':
deviceconfig[idx].update({'hide': True})
elif idx in deviceconfig.keys() and 'hide' in deviceconfig[idx]:
deviceconfig[idx].pop('hide')

if willReportState != 'on':
deviceconfig[idx].update({'report_state': False})
elif idx in deviceconfig.keys() and 'report_state' in deviceconfig[idx]:
deviceconfig[idx].pop('report_state')

if challenge == 'ackNeeded':
deviceconfig[idx].update({'ack': True})
elif idx in deviceconfig.keys() and 'ack' in deviceconfig[idx]:
deviceconfig[idx].pop('ack')

if room is not None:
if room != '':
deviceconfig[idx].update({'room': room})
elif idx in deviceconfig.keys() and 'room' in deviceconfig[idx]:
deviceconfig[idx].pop('room')

if nicknames is not None:
if nicknames != '':
names = nicknames.split(", ")
names = list(filter(None, names))
deviceconfig[idx].update({'nicknames':names})
elif idx in deviceconfig.keys() and 'nicknames' in deviceconfig[idx]:
deviceconfig[idx].pop('nicknames')

if devicetype is not None:
if devicetype != 'default':
deviceconfig[idx].update({'devicetype': devicetype})
elif idx in deviceconfig.keys() and 'devicetype' in deviceconfig[idx]:
deviceconfig[idx].pop('devicetype')

if minThreehold is not None:
if minThreehold != '':
deviceconfig[idx].update({'minThreehold': int(minThreehold)})
elif idx in deviceconfig.keys() and 'minThreehold' in deviceconfig[idx]:
deviceconfig[idx].pop('minThreehold')

if maxThreehold is not None:
if maxThreehold != '':
deviceconfig[idx].update({'maxThreehold': int(maxThreehold)})
elif idx in deviceconfig.keys() and 'maxThreehold' in deviceconfig[idx]:
deviceconfig[idx].pop('maxThreehold')

if actual_temp_idx is not None:
if actual_temp_idx != '':
deviceconfig[idx].update({'actual_temp_idx': actual_temp_idx})
elif idx in deviceconfig.keys() and 'actual_temp_idx' in deviceconfig[idx]:
deviceconfig[idx].pop('actual_temp_idx')

if selector_modes_idx is not None:
if selector_modes_idx != '':
deviceconfig[idx].update({'selector_modes_idx': selector_modes_idx})
elif idx in deviceconfig.keys() and 'selector_modes_idx' in deviceconfig[idx]:
deviceconfig[idx].pop('selector_modes_idx')

if camurl is not None:
if camurl != '':
deviceconfig[idx].update({'camurl': camurl})
elif idx in deviceconfig.keys() and 'camurl' in deviceconfig[idx]:
deviceconfig[idx].pop('camurl')

if deviceconfig[idx] == {}:
deviceconfig.pop(idx)

dbuser.device_config.update(deviceconfig)
db.session.add(dbuser)
db.session.commit()

armedhome = request.form.get('armedhome')
armedaway = request.form.get('armedaway')
if (armedhome is not None) or (armedaway is not None):
armedhome = armedhome.split(", ")
armedaway = armedaway.split(", ")
armhome = list(filter(None, armedhome))
armaway = list(filter(None, armedaway))
dbsettings.armlevels.update({'armhome': armhome, 'armaway': armaway})

db.session.add(dbsettings)
db.session.commit()

logger.info("Device settings saved")

return redirect(url_for('devices'))

if request.method == "GET":

return render_template('devices.html',
user=User.query.filter_by(username=current_user.username).first(),
dbsettings=dbsettings,
reportstate=reportstate,
devices=devices,
_csrf_token=session['_csrf_token']
)

return render_template('devices.html',
user=dbuser,
dbsettings=dbsettings,
reportstate=reportstate,
devices=devices,
_csrf_token=session['_csrf_token']
)


@login_required
Expand Down Expand Up @@ -78,7 +193,7 @@ def settings():

dbuser = User.query.filter_by(username=current_user.username).first()
dbsettings = Settings.query.get_or_404(1)

if request.form['submit'] == 'save_user_settings':
dbuser.domo_url = request.form.get('domourl')
dbuser.domouser = request.form.get('domouser')
Expand All @@ -100,10 +215,6 @@ def settings():
dbsettings.use_ssl = (request.form.get('ssl') == 'true')
dbsettings.ssl_cert = request.form.get('sslcert')
dbsettings.ssl_key = request.form.get('sslkey')
armhome = request.form.get('armhome')
armaway = request.form.get('armaway')

dbsettings.armlevels.update({'armhome': armhome, 'armaway': armaway})

db.session.add(dbsettings)
db.session.commit()
Expand All @@ -122,8 +233,8 @@ def settings():
return redirect(url_for('settings'))

add_new_user = User(email=newemail, username=newuser, password=newpass,
roomplan='0', domo_url='http://192.168.1.13:8080', domouser='domoticz', domopass='password',
admin=newadmin, googleassistant=newgoogleassistant, authtoken=generateToken(newuser))
roomplan='0', domo_url='http://192.168.1.99:8080', domouser='domoticz', domopass='password',
admin=newadmin, googleassistant=newgoogleassistant, authtoken=generateToken(newuser), device_config={})

db.session.add(add_new_user)
db.session.commit()
Expand Down
39 changes: 39 additions & 0 deletions static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,45 @@ h6 {
/*--------------------------------------------------------------
# Override some default Bootstrap stylings
--------------------------------------------------------------*/
.forkme {
margin: 0 -21px 0 0;
}
.logging_window{
display: block;
padding: 9.5px;
font-size: 16px;
line-height: 1.42857143;
color: #333;
word-break: break-all;
word-wrap: break-word;
background-color: #f5f5f5;
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
height: 400px;
margin: auto;
}
.icon {
border: transparent;
border-radius: 15px;
box-shadow: 0px 0 30px rgba(1, 41, 112, 0.1);
}
.click {
cursor: pointer;
}
.icon:hover {
background: #f6f9ff;
}
.input-group button {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}

.form-control[type="color"]{
display: inline-block;
padding: 0.10rem;
}

/* Dropdown menus */
.dropdown-menu {
border-radius: 4px;
Expand Down
3 changes: 1 addition & 2 deletions static/js/smarthome.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,7 @@ function getVersion() {
var url = "/api?type=command&param=getversion"
requestAPI(url).then(jsonData => {
var data = jsonData
console.log(data.version);
$('#dzga-version').html('23.3')
$('#dzga-version').html('23.6')
$('#dz-version').html(data.version)
});
}
Expand Down
Loading
Loading