Skip to content

Commit aa5a7b7

Browse files
committed
Random auth keys
Remember last device
1 parent b83c23d commit aa5a7b7

15 files changed

+207
-38
lines changed

data/com.github.tkashkin.boiler.gschema.xml

+12
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,16 @@
88
<default>-1</default>
99
</key>
1010
</schema>
11+
12+
<schema path="/com/github/tkashkin/boiler/devices/" id="com.github.tkashkin.boiler.devices" gettext-domain="com.github.tkashkin.boiler">
13+
<key name="last-device" type="s">
14+
<default>''</default>
15+
</key>
16+
</schema>
17+
18+
<schema path="/com/github/tkashkin/boiler/dev/redmond/rk-g2xx/" id="com.github.tkashkin.boiler.dev.redmond.rk-g2xx" gettext-domain="com.github.tkashkin.boiler">
19+
<key name="auth-key" type="s">
20+
<default>''</default>
21+
</key>
22+
</schema>
1123
</schemalist>

data/screenshots/connect.png

11.3 KB
Loading

data/screenshots/kettle_100.png

17.1 KB
Loading

data/screenshots/kettle_31.png

15.4 KB
Loading

data/screenshots/kettle_60.png

17.2 KB
Loading

data/screenshots/kettle_90.png

17.5 KB
Loading

debian/control

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ Standards-Version: 4.1.4
1313

1414
Package: com.github.tkashkin.boiler
1515
Architecture: any
16-
Depends: ${misc:Depends}, ${shlibs:Depends}, bluez, bluez-tools
16+
Depends: ${misc:Depends}, ${shlibs:Depends}, bluez
1717
Description: Simple app to control Redmond Skykettle RK-G200S

src/app.vala

+7
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ namespace Boiler
3939
Intl.bindtextdomain(ProjectConfig.GETTEXT_PACKAGE, ProjectConfig.GETTEXT_DIR);
4040
Intl.textdomain(ProjectConfig.GETTEXT_PACKAGE);
4141

42+
var rk_g2xx_auth = Settings.Dev.Redmond.RK_G2XX.get_instance();
43+
if(rk_g2xx_auth.auth_key == "")
44+
{
45+
var bytes = Utils.random_bytes(8);
46+
rk_g2xx_auth.auth_key = Converter.bin_to_hex(bytes, ' ');
47+
}
48+
4249
return app.run(args);
4350
}
4451
}

src/devices/abstract/BTKettle.vala

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ public abstract class Boiler.Devices.Abstract.BTKettle: Object
77
public Bluez.Device bt_device { get; construct; }
88
public Bluez.Manager btmgr { get; construct; }
99

10+
public string name { get; protected set; default = ""; }
11+
public string description { get; protected set; default = ""; }
12+
public string status { get; protected set; default = ""; }
13+
public string? pairing_info { get; protected set; default = null; }
14+
15+
public bool is_paired { get; protected set; default = true; }
1016
public bool is_connected { get; protected set; default = false; }
11-
public int temperature { get; protected set; default = -1; }
17+
1218
public bool is_boiling { get; protected set; default = false; }
13-
public string status { get; protected set; default = ""; }
19+
public int temperature { get; protected set; default = -1; }
1420

1521
public abstract void start_boiling();
1622
public abstract void stop_boiling();

src/devices/kettle/redmond/RK_G2XX.vala

+58-19
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ public class Boiler.Devices.Kettle.Redmond.RK_G2XX: Boiler.Devices.Abstract.BTKe
66
{
77
public const string[] DEVICES = { "RK-G200S", "RK-G210S", "RK-G211S" };
88

9+
private uint8[] auth_key = DEFAULT_AUTH_KEY;
10+
911
private bool is_authenticated = false;
12+
13+
private bool auth_thread_running = false;
1014
private bool status_thread_running = false;
15+
1116
private uint8 counter = 0;
1217

1318
private HashTable<string, Variant> _params = new HashTable<string, Variant>(str_hash, null);
@@ -19,6 +24,20 @@ public class Boiler.Devices.Kettle.Redmond.RK_G2XX: Boiler.Devices.Abstract.BTKe
1924
{
2025
Object(bt_device: device, btmgr: btmgr);
2126

27+
name = bt_device.name;
28+
description = bt_device.address;
29+
pairing_info = _("Your PC is not paired to the kettle.\nHold kettle power button for 5 seconds");
30+
31+
var key = Boiler.Settings.Dev.Redmond.RK_G2XX.get_instance().auth_key;
32+
33+
if(key != "")
34+
{
35+
if(!Converter.hex_to_bin(key, out auth_key, ' '))
36+
{
37+
auth_key = DEFAULT_AUTH_KEY;
38+
}
39+
}
40+
2241
bt_connect();
2342
}
2443

@@ -143,23 +162,35 @@ public class Boiler.Devices.Kettle.Redmond.RK_G2XX: Boiler.Devices.Abstract.BTKe
143162
{
144163
log("Authenticating...");
145164

146-
while(true)
147-
{
148-
if(!is_connected) return;
149-
150-
var res = send_command(Command.AUTH, AUTH_KEY);
151-
if(res.length < 4 || res[3] == 0)
152-
{
153-
log("Authentication failed, retrying...");
154-
}
155-
else
165+
if(auth_thread_running) return;
166+
new Thread<void*>("RK-G2XX-auth-thread", () => {
167+
while(true)
156168
{
157-
log("Authentication succeeded");
158-
is_authenticated = true;
159-
start_status_thread();
160-
break;
169+
auth_thread_running = true;
170+
if(!is_connected) break;
171+
172+
var res = send_command(Command.AUTH, auth_key);
173+
if(res.length < 4 || res[3] == 0)
174+
{
175+
log("Authentication failed, retrying...");
176+
is_paired = false;
177+
Thread.usleep(1000000);
178+
}
179+
else
180+
{
181+
log("Authentication succeeded");
182+
is_authenticated = true;
183+
is_paired = true;
184+
description = @"$(bt_device.address) (fw $(get_fw_version()))";
185+
start_status_thread();
186+
break;
187+
}
161188
}
162-
}
189+
190+
auth_thread_running = false;
191+
192+
return null;
193+
});
163194
}
164195

165196
public override void start_boiling()
@@ -177,6 +208,13 @@ public class Boiler.Devices.Kettle.Redmond.RK_G2XX: Boiler.Devices.Abstract.BTKe
177208
is_boiling = false;
178209
}
179210

211+
private string get_fw_version()
212+
{
213+
log("Getting firmware version...");
214+
var res = send_command(Command.FW_VERSION, {});
215+
return @"$(res[3]).$(res[4])";
216+
}
217+
180218
private void start_status_thread()
181219
{
182220
if(status_thread_running) return;
@@ -213,13 +251,13 @@ public class Boiler.Devices.Kettle.Redmond.RK_G2XX: Boiler.Devices.Abstract.BTKe
213251
private const string RES_CHAR_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
214252
private const string CMD_DESC_UUID = "00002902-0000-1000-8000-00805f9b34fb";
215253

216-
private const uint8[] COMMAND_START = { 0x55 };
217-
private const uint8[] COMMAND_END = { 0xAA };
218-
private const uint8[] AUTH_KEY = { 0xB5, 0x4C, 0x75, 0xB1, 0xB4, 0x0C, 0x88, 0xEF }; // maybe random
254+
private const uint8[] COMMAND_START = { 0x55 };
255+
private const uint8[] COMMAND_END = { 0xAA };
256+
private const uint8[] DEFAULT_AUTH_KEY = { 0xB5, 0x4C, 0x75, 0xB1, 0xB4, 0x0C, 0x88, 0xEF };
219257

220258
private enum Command
221259
{
222-
AUTH, STATUS, START_BOILING, STOP_BOILING;
260+
AUTH, STATUS, START_BOILING, STOP_BOILING, FW_VERSION;
223261

224262
public uint8 byte()
225263
{
@@ -229,6 +267,7 @@ public class Boiler.Devices.Kettle.Redmond.RK_G2XX: Boiler.Devices.Abstract.BTKe
229267
case Command.STATUS: return 0x06;
230268
case Command.START_BOILING: return 0x03;
231269
case Command.STOP_BOILING: return 0x04;
270+
case Command.FW_VERSION: return 0x01;
232271
}
233272
return 0x00;
234273
}

src/meson.build

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ shared_sources = [
3838
icons_gresource,
3939
css_gresource,
4040

41+
'utils/Utils.vala',
4142
'utils/Settings.vala',
4243
'utils/Converter.vala',
4344

src/ui/views/connect/DeviceRow.vala

+15-1
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,25 @@ class Boiler.UI.Views.Connect.DeviceRow: Gtk.ListBoxRow
3737
{
3838
var connect_btn = new Button.with_label(_("Connect"));
3939

40+
var settings = Boiler.Settings.Devices.get_instance();
41+
4042
connect_btn.clicked.connect(() => {
4143
kettle = Devices.connect(device);
42-
if(kettle != null) connected(kettle);
44+
if(kettle != null)
45+
{
46+
settings.last_device = device.address;
47+
connected(kettle);
48+
}
4349
});
4450

51+
if(settings.last_device == device.address)
52+
{
53+
Idle.add(() => {
54+
connect_btn.clicked();
55+
return Source.REMOVE;
56+
});
57+
}
58+
4559
hbox.add(connect_btn);
4660
}
4761

src/ui/views/kettle/KettleView.vala

+49-15
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ namespace Boiler.UI.Views.Kettle
1717
private Label temp_label;
1818
private Label boil_label;
1919

20-
private Label address_label;
20+
private Label title_label;
21+
private Label description_label;
2122
private Spinner spinner;
2223

24+
private InfoBar pairing_info;
25+
2326
private CssProvider? temp_style = new CssProvider();
2427
private CssProvider? btn_temp_style = new CssProvider();
2528
private bool updating = false;
@@ -32,14 +35,14 @@ namespace Boiler.UI.Views.Kettle
3235

3336
construct
3437
{
35-
var title_label = new Label(kettle.bt_device.name);
38+
title_label = new Label(kettle.name);
3639
title_label.get_style_context().add_class(Granite.STYLE_CLASS_PRIMARY_LABEL);
3740
title_label.hexpand = true;
3841

39-
address_label = new Label(kettle.bt_device.address);
40-
address_label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
41-
address_label.get_style_context().add_class("small");
42-
address_label.hexpand = true;
42+
description_label = new Label(kettle.description);
43+
description_label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
44+
description_label.get_style_context().add_class("small");
45+
description_label.hexpand = true;
4346

4447
temp_label = new Label("");
4548
boil_label = new Label("");
@@ -71,16 +74,44 @@ namespace Boiler.UI.Views.Kettle
7174
spinner.set_size_request(16, 16);
7275
spinner.halign = Align.END;
7376

74-
attach(title_label, 0, 0, 3, 1);
75-
attach(address_label, 0, 1, 3, 1);
76-
attach(image, 0, 0, 1, 2);
77-
attach(spinner, 2, 0, 1, 2);
78-
attach(boil_btn, 1, 2, 1, 1);
77+
if(kettle.pairing_info != null)
78+
{
79+
pairing_info = new InfoBar();
80+
pairing_info.show_close_button = false;
81+
pairing_info.margin_bottom = 4;
82+
pairing_info.message_type = MessageType.WARNING;
83+
pairing_info.get_content_area().add(new Label(kettle.pairing_info));
84+
85+
#if GTK_3_22
86+
pairing_info.revealed = false;
87+
#else
88+
pairing_info.visible = false;
89+
#endif
90+
91+
kettle.notify["is-paired"].connect(() => {
92+
#if GTK_3_22
93+
pairing_info.revealed = !kettle.is_paired;
94+
#else
95+
pairing_info.visible = !kettle.is_paired;
96+
#endif
97+
});
98+
99+
attach(pairing_info, 0, 0, 3, 1);
100+
}
101+
102+
attach(title_label, 0, 1, 3, 1);
103+
attach(description_label, 0, 2, 3, 1);
104+
attach(image, 0, 1, 1, 2);
105+
attach(spinner, 2, 1, 1, 2);
106+
attach(boil_btn, 1, 3, 1, 1);
107+
108+
kettle.notify["name"].connect(update);
109+
kettle.notify["description"].connect(update);
110+
kettle.notify["status"].connect(update);
79111

80112
kettle.notify["is-connected"].connect(update);
81113
kettle.notify["temperature"].connect(update);
82114
kettle.notify["is-boiling"].connect(update);
83-
kettle.notify["status"].connect(update);
84115

85116
update();
86117

@@ -108,14 +139,17 @@ namespace Boiler.UI.Views.Kettle
108139

109140
lock(kettle)
110141
{
111-
boil_btn.sensitive = kettle.is_connected;
112-
spinner.active = !kettle.is_connected;
142+
boil_btn.sensitive = kettle.is_connected && kettle.is_paired;
143+
spinner.active = !kettle.is_connected || !kettle.is_paired;
113144
spinner.queue_draw();
114145

115146
boil_btn.active = kettle.is_connected && kettle.is_boiling;
116147
temp_label.label = kettle.temperature > 0 ? @"$(kettle.temperature) \u2103" : "";
117148
boil_label.label = kettle.is_boiling ? _("Disable") : _("Enable");
118-
address_label.tooltip_text = kettle.status;
149+
150+
title_label.label = kettle.name;
151+
description_label.label = kettle.description;
152+
description_label.tooltip_text = kettle.status;
119153

120154
if(window == null || window.get_toplevel() == null)
121155
{

src/utils/Settings.vala

+40
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,44 @@ namespace Boiler.Settings
2323
return instance;
2424
}
2525
}
26+
27+
public class Devices: Granite.Services.Settings
28+
{
29+
public string last_device { get; set; }
30+
31+
public Devices()
32+
{
33+
base(ProjectConfig.PROJECT_NAME + ".devices");
34+
}
35+
36+
private static Devices? instance;
37+
public static unowned Devices get_instance()
38+
{
39+
if(instance == null)
40+
{
41+
instance = new Devices();
42+
}
43+
return instance;
44+
}
45+
}
46+
47+
public class Dev.Redmond.RK_G2XX: Granite.Services.Settings
48+
{
49+
public string auth_key { get; set; }
50+
51+
public RK_G2XX()
52+
{
53+
base(ProjectConfig.PROJECT_NAME + ".dev.redmond.rk-g2xx");
54+
}
55+
56+
private static RK_G2XX? instance;
57+
public static unowned RK_G2XX get_instance()
58+
{
59+
if(instance == null)
60+
{
61+
instance = new RK_G2XX();
62+
}
63+
return instance;
64+
}
65+
}
2666
}

src/utils/Utils.vala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using GLib;
2+
3+
namespace Boiler.Utils
4+
{
5+
public uint8[] random_bytes(uint length)
6+
{
7+
uint8[] bytes = new uint8[length];
8+
9+
for(uint i = 0; i < length; i++)
10+
{
11+
bytes[i] = (uint8) Random.int_range(0, 256);
12+
}
13+
14+
return bytes;
15+
}
16+
}

0 commit comments

Comments
 (0)