prefer controlData.json over devices.txt
This commit is contained in:
parent
aad54b0b93
commit
97aaee50af
1 changed files with 87 additions and 21 deletions
108
src/main.zig
108
src/main.zig
|
@ -7,7 +7,7 @@ const DeviceAction = enum {
|
|||
toggle,
|
||||
};
|
||||
|
||||
fn sendWemoCommand(ip: []const u8, action: DeviceAction, allocator: std.mem.Allocator) !void {
|
||||
fn sendWemoCommand(url_base: []const u8, action: DeviceAction, allocator: std.mem.Allocator) !void {
|
||||
const state = switch (action) {
|
||||
.on => "1",
|
||||
.off => "0",
|
||||
|
@ -27,13 +27,13 @@ fn sendWemoCommand(ip: []const u8, action: DeviceAction, allocator: std.mem.Allo
|
|||
, .{state});
|
||||
defer allocator.free(soap_body);
|
||||
|
||||
const url = try std.fmt.allocPrint(allocator, "http://{s}/upnp/control/basicevent1", .{ip});
|
||||
const url = try std.fmt.allocPrint(allocator, "{s}/upnp/control/basicevent1", .{url_base});
|
||||
defer allocator.free(url);
|
||||
|
||||
var client = std.http.Client{ .allocator = allocator };
|
||||
defer client.deinit();
|
||||
|
||||
std.log.debug("sending wemo command to {s}, action={s}", .{ ip, @tagName(action) });
|
||||
std.log.debug("sending wemo command to {s}, action={s}", .{ url_base, @tagName(action) });
|
||||
const res = try client.fetch(.{
|
||||
.method = .POST,
|
||||
.payload = soap_body,
|
||||
|
@ -47,18 +47,72 @@ fn sendWemoCommand(ip: []const u8, action: DeviceAction, allocator: std.mem.Allo
|
|||
if (res.status == .ok) {
|
||||
var stdout_writer = std.fs.File.stdout().writer(&.{});
|
||||
const stdout = &stdout_writer.interface;
|
||||
try stdout.print("WeMo command sent: IP={s}, Action={s}\n", .{ ip, @tagName(action) });
|
||||
try stdout.print("WeMo command sent: url={s}, Action={s}\n", .{ url_base, @tagName(action) });
|
||||
} else {
|
||||
var stderr_writer = std.fs.File.stderr().writer(&.{});
|
||||
const stderr = &stderr_writer.interface;
|
||||
try stderr.print("WeMo command sent: IP={s}, Action={s}\n", .{ ip, @tagName(action) });
|
||||
try stderr.print("WeMo command sent: url={s}, Action={s}\n", .{ url_base, @tagName(action) });
|
||||
}
|
||||
}
|
||||
|
||||
fn loadDeviceConfig(allocator: std.mem.Allocator) !std.StringHashMap([]const u8) {
|
||||
var devices = std.StringHashMap([]const u8).init(allocator);
|
||||
|
||||
const file = try std.fs.cwd().openFile("devices.txt", .{});
|
||||
var stderr_writer = std.fs.File.stderr().writer(&.{});
|
||||
const stderr = &stderr_writer.interface;
|
||||
// Try controlData.json first
|
||||
if (std.fs.cwd().openFile("controlData.json", .{})) |file| {
|
||||
defer file.close();
|
||||
|
||||
const content = try file.readToEndAlloc(allocator, 1024 * 1024);
|
||||
defer allocator.free(content);
|
||||
|
||||
const parsed = std.json.parseFromSlice(std.json.Value, allocator, content, .{}) catch |err| {
|
||||
try stderr.print(
|
||||
"Failed to parse controlData.json: {}. Ignoring controlData.json, looking for devices.txt",
|
||||
.{err},
|
||||
);
|
||||
return loadDevicesFromTxt(allocator);
|
||||
};
|
||||
defer parsed.deinit();
|
||||
|
||||
const root = parsed.value.object;
|
||||
const device_array = root.get("devices").?.array;
|
||||
|
||||
for (device_array.items) |device| {
|
||||
const device_obj = device.object;
|
||||
const name = device_obj.get("name").?.string;
|
||||
const url = device_obj.get("url").?.string;
|
||||
|
||||
if (name.len > 0) {
|
||||
const name_copy = try allocator.alloc(u8, name.len);
|
||||
_ = std.ascii.lowerString(name_copy, name);
|
||||
try devices.put(name_copy, try allocator.dupe(u8, url));
|
||||
std.log.debug("Loaded device: '{s}' -> '{s}'\n", .{ name, url });
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
} else |_| {
|
||||
return loadDevicesFromTxt(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
fn loadDevicesFromTxt(allocator: std.mem.Allocator) !std.StringHashMap([]const u8) {
|
||||
var devices = std.StringHashMap([]const u8).init(allocator);
|
||||
|
||||
const file = std.fs.cwd().openFile("devices.txt", .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => {
|
||||
var stderr_writer = std.fs.File.stderr().writer(&.{});
|
||||
const stderr = &stderr_writer.interface;
|
||||
try stderr.print(
|
||||
\\Error: could not load configuration. Please make sure controlData.json or
|
||||
\\devices.txt is available in the directory with this program
|
||||
, .{});
|
||||
return error.ConfigurationNotAvailable;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
const content = try file.readToEndAlloc(allocator, 1024 * 1024);
|
||||
|
@ -71,9 +125,11 @@ fn loadDeviceConfig(allocator: std.mem.Allocator) !std.StringHashMap([]const u8)
|
|||
|
||||
if (std.mem.indexOf(u8, trimmed, "=")) |eq_pos| {
|
||||
const name = std.mem.trim(u8, trimmed[0..eq_pos], " \t");
|
||||
const ip = std.mem.trim(u8, trimmed[eq_pos + 1 ..], " \t");
|
||||
if (name.len > 0 and ip.len > 0) {
|
||||
try devices.put(try allocator.dupe(u8, name), try allocator.dupe(u8, ip));
|
||||
const url_base = std.mem.trim(u8, trimmed[eq_pos + 1 ..], " \t");
|
||||
if (name.len > 0 and url_base.len > 0) {
|
||||
const name_copy = try allocator.alloc(u8, name.len);
|
||||
_ = std.ascii.lowerString(name_copy, name);
|
||||
try devices.put(name, try allocator.dupe(u8, url_base));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,19 +148,23 @@ fn parseAction(action_words: [][]const u8) ?DeviceAction {
|
|||
return null;
|
||||
}
|
||||
|
||||
fn extractDevice(allocator: std.mem.Allocator, object_words: [][]const u8, devices: *std.StringHashMap([]const u8)) ?[]const u8 {
|
||||
fn extractDevice(allocator: std.mem.Allocator, object_words: [][]const u8, devices: *std.StringHashMap([]const u8)) !?[]const u8 {
|
||||
if (object_words.len == 0) return null;
|
||||
|
||||
if (object_words.len == 1) {
|
||||
return devices.get(object_words[0]);
|
||||
} else if (object_words.len == 2) {
|
||||
var device_name = std.ArrayList(u8){};
|
||||
defer device_name.deinit(allocator);
|
||||
device_name.appendSlice(allocator, object_words[0]) catch return null;
|
||||
device_name.append(allocator, ' ') catch return null;
|
||||
device_name.appendSlice(allocator, object_words[1]) catch return null;
|
||||
return devices.get(device_name.items);
|
||||
var total_size: usize = 0;
|
||||
for (object_words) |word|
|
||||
total_size += word.len + 1;
|
||||
var allocating_writer = try std.Io.Writer.Allocating.initCapacity(allocator, total_size);
|
||||
defer allocating_writer.deinit();
|
||||
const writer = &allocating_writer.writer;
|
||||
for (object_words, 1..) |word, i| {
|
||||
var buf: [256]u8 = undefined;
|
||||
const lower_string = std.ascii.lowerString(&buf, word);
|
||||
try writer.print("{s}{s}", .{ lower_string, if (i < object_words.len) " " else "" });
|
||||
}
|
||||
|
||||
if (devices.get(allocating_writer.written())) |url_base| return url_base;
|
||||
std.log.warn("Could not find device '{s}'", .{allocating_writer.written()});
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -165,9 +225,15 @@ pub fn main() !void {
|
|||
};
|
||||
defer gpa.allocator().free(object_words);
|
||||
|
||||
std.debug.print("Object words: ", .{});
|
||||
for (object_words) |word| {
|
||||
std.debug.print("'{s}' ", .{word});
|
||||
}
|
||||
std.debug.print("\n", .{});
|
||||
|
||||
if (parseAction(action_words)) |action| {
|
||||
if (extractDevice(allocator, object_words, &devices)) |ip| {
|
||||
try sendWemoCommand(ip, action, gpa.allocator());
|
||||
if (try extractDevice(allocator, object_words, &devices)) |url_base| {
|
||||
try sendWemoCommand(url_base, action, gpa.allocator());
|
||||
} else {
|
||||
try stdout.print("Device not found in configuration\n", .{});
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue