Added more google traits and implemented execution function

This commit is contained in:
Ziver Koc 2021-08-30 01:16:00 +02:00
parent 08fd1b6edd
commit 4339239c33
8 changed files with 274 additions and 32 deletions

View file

@ -24,13 +24,16 @@ import com.google.home.graph.v1.HomeGraphApiServiceProto;
import com.google.protobuf.Struct; import com.google.protobuf.Struct;
import com.google.protobuf.Value; import com.google.protobuf.Value;
import org.json.JSONObject; import org.json.JSONObject;
import se.hal.EventControllerManager;
import se.hal.HalContext; import se.hal.HalContext;
import se.hal.intf.HalAbstractDevice; import se.hal.intf.HalAbstractDevice;
import se.hal.plugin.assistant.google.trait.DeviceTrait; import se.hal.plugin.assistant.google.trait.DeviceTrait;
import se.hal.plugin.assistant.google.trait.DeviceTraitFactory; import se.hal.plugin.assistant.google.trait.DeviceTraitFactory;
import se.hal.plugin.assistant.google.trait.OnOffTrait;
import se.hal.plugin.assistant.google.type.DeviceType; import se.hal.plugin.assistant.google.type.DeviceType;
import se.hal.struct.Event; import se.hal.struct.Event;
import se.hal.struct.Sensor; import se.hal.struct.Sensor;
import se.hal.struct.devicedata.OnOffEventData;
import zutil.db.DBConnection; import zutil.db.DBConnection;
import zutil.log.LogUtil; import zutil.log.LogUtil;
import zutil.net.http.page.oauth.OAuth2Registry.TokenRegistrationListener; import zutil.net.http.page.oauth.OAuth2Registry.TokenRegistrationListener;
@ -64,8 +67,6 @@ public class SmartHomeImpl extends SmartHomeApp implements TokenRegistrationList
/** /**
* https://developers.google.com/assistant/smarthome/reference/intent/sync * https://developers.google.com/assistant/smarthome/reference/intent/sync
*
* TODO: https://developers.google.com/assistant/smarthome/traits/temperaturesetting
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@ -124,10 +125,10 @@ public class SmartHomeImpl extends SmartHomeApp implements TokenRegistrationList
// Set custom data // Set custom data
/*JSONObject customDataJson = new JSONObject(); JSONObject customDataJson = new JSONObject();
customDataJson.put("type", device.getClass().getSimpleName()); customDataJson.put("type", device.getClass().getSimpleName());
customDataJson.put("id", device.getId()); customDataJson.put("id", device.getId());
deviceBuilder.setCustomData(customDataJson);*/ deviceBuilder.setCustomData(customDataJson);
res.payload.devices[i] = deviceBuilder.build(); res.payload.devices[i] = deviceBuilder.build();
} }
@ -181,12 +182,11 @@ public class SmartHomeImpl extends SmartHomeApp implements TokenRegistrationList
try { try {
logger.fine("Received query request for: type=" + deviceRequest.getId()); logger.fine("Received query request for: type=" + deviceRequest.getId());
String[] deviceIdArray = deviceRequest.getId().split("-"); if (!deviceRequest.getCustomData().containsKey("type") && !deviceRequest.getCustomData().containsKey("id"))
if (deviceIdArray.length != 2) throw new IllegalArgumentException("Device Type and ID was no supplied in customData: " + deviceRequest.getId());
throw new IllegalArgumentException("Invalid device ID: " + deviceRequest.getId());
String deviceTypeStr = deviceIdArray[0]; String deviceTypeStr = (String) deviceRequest.getCustomData().get("type");
long deviceId = Long.parseLong(deviceIdArray[1]); // Get the number in the id "Sensor-<number>" long deviceId = Long.parseLong((String) deviceRequest.getCustomData().get("id"));
HalAbstractDevice device; HalAbstractDevice device;
switch (deviceTypeStr) { switch (deviceTypeStr) {
@ -229,37 +229,47 @@ public class SmartHomeImpl extends SmartHomeApp implements TokenRegistrationList
*/ */
@Override @Override
public ExecuteResponse onExecute(ExecuteRequest executeRequest, Map<?, ?> headers) { public ExecuteResponse onExecute(ExecuteRequest executeRequest, Map<?, ?> headers) {
logger.fine("Received execute request."); DBConnection db = HalContext.getDB();
ExecuteResponse res = new ExecuteResponse(); ExecuteResponse res = new ExecuteResponse();
List<ExecuteResponse.Payload.Commands> commandsResponse = new ArrayList<>(); List<ExecuteResponse.Payload.Commands> commandsResponse = new ArrayList<>();
List<String> successfulDevices = new ArrayList<>();
Map<String, Object> states = new HashMap<>();
ExecuteRequest.Inputs.Payload.Commands[] commands = for (ExecuteRequest.Inputs.Payload.Commands command : ((ExecuteRequest.Inputs) executeRequest.inputs[0]).payload.commands) {
((ExecuteRequest.Inputs) executeRequest.inputs[0]).payload.commands; for (ExecuteRequest.Inputs.Payload.Commands.Devices deviceRequest : command.devices) {
/* for (ExecuteRequest.Inputs.Payload.Commands command : commands) {
for (ExecuteRequest.Inputs.Payload.Commands.Devices device : command.devices) {
try { try {
states = database.execute(userId, device.id, command.execution[0]); if (!deviceRequest.getCustomData().containsKey("type") && !deviceRequest.getCustomData().containsKey("id"))
successfulDevices.add(device.id); throw new IllegalArgumentException("Device Type and ID was no supplied in customData: " + deviceRequest.getId());
ReportState.makeRequest(this, userId, device.id, states);
String deviceTypeStr = (String) deviceRequest.getCustomData().get("type");
long deviceId = Long.parseLong((String) deviceRequest.getCustomData().get("id"));
HalAbstractDevice device;
switch (deviceTypeStr) {
case "Sensor": device = Sensor.getSensor(db, deviceId); break;
case "Event": device = Event.getEvent(db, deviceId); break;
default: throw new IllegalArgumentException("Unknown device type: " + deviceTypeStr);
}
for (ExecuteRequest.Inputs.Payload.Commands.Execution execution : command.execution) {
if ("action.devices.traits.OnOff".equals(execution.command)) { // TODO: This looks ugly!
new OnOffTrait().execute(device, execution);
} else
throw new UnsupportedOperationException("Unsupported command requested: " + execution.command);
}
ExecuteResponse.Payload.Commands successfulCommands = new ExecuteResponse.Payload.Commands();
successfulCommands.status = "SUCCESS";
successfulCommands.ids = new String[]{deviceRequest.id};
commandsResponse.add(successfulCommands);
} catch (Exception e) { } catch (Exception e) {
ExecuteResponse.Payload.Commands failedDevice = new ExecuteResponse.Payload.Commands(); ExecuteResponse.Payload.Commands failedDevice = new ExecuteResponse.Payload.Commands();
failedDevice.ids = new String[]{device.id}; failedDevice.ids = new String[]{deviceRequest.id};
failedDevice.status = "ERROR"; failedDevice.status = "ERROR";
failedDevice.setErrorCode(e.getMessage()); failedDevice.setErrorCode(e.getMessage());
commandsResponse.add(failedDevice); commandsResponse.add(failedDevice);
} }
} }
}*/ }
ExecuteResponse.Payload.Commands successfulCommands = new ExecuteResponse.Payload.Commands();
successfulCommands.status = "SUCCESS";
successfulCommands.setStates(states);
successfulCommands.ids = successfulDevices.toArray(new String[]{});
commandsResponse.add(successfulCommands);
res.requestId = executeRequest.requestId; res.requestId = executeRequest.requestId;
ExecuteResponse.Payload payload = new ExecuteResponse.Payload( ExecuteResponse.Payload payload = new ExecuteResponse.Payload(

View file

@ -24,6 +24,8 @@
package se.hal.plugin.assistant.google.trait; package se.hal.plugin.assistant.google.trait;
import com.google.actions.api.smarthome.ExecuteRequest;
import se.hal.intf.HalAbstractDevice;
import se.hal.intf.HalDeviceConfig; import se.hal.intf.HalDeviceConfig;
import se.hal.intf.HalDeviceData; import se.hal.intf.HalDeviceData;
@ -42,4 +44,6 @@ public abstract class DeviceTrait {
public abstract HashMap<String, Object> generateSyncResponse(HalDeviceConfig config); public abstract HashMap<String, Object> generateSyncResponse(HalDeviceConfig config);
public abstract HashMap<String, Object> generateQueryResponse(HalDeviceData data); public abstract HashMap<String, Object> generateQueryResponse(HalDeviceData data);
public void execute(HalAbstractDevice device, ExecuteRequest.Inputs.Payload.Commands.Execution execution) {}
} }

View file

@ -46,17 +46,20 @@ public class DeviceTraitFactory {
switch (device.getDeviceData().getClass().getName()) { switch (device.getDeviceData().getClass().getName()) {
case "se.hal.struct.devicedata.DimmerEventData": case "se.hal.struct.devicedata.DimmerEventData":
case "se.hal.struct.devicedata.OnOffEventData": case "se.hal.struct.devicedata.OnOffEventData":
return new DeviceTrait[]{}; return new DeviceTrait[]{new OnOffTrait()};
case "se.hal.struct.devicedata.OpenClosedEventData":
return new DeviceTrait[]{new OpenCloseTrait()};
case "se.hal.struct.devicedata.PowerConsumptionSensorData": case "se.hal.struct.devicedata.PowerConsumptionSensorData":
case "se.hal.struct.devicedata.LightSensorData": case "se.hal.struct.devicedata.LightSensorData":
return new DeviceTrait[]{}; return new DeviceTrait[]{new SensorStateTrait()};
case "se.hal.struct.devicedata.HumiditySensorData": case "se.hal.struct.devicedata.HumiditySensorData":
return new DeviceTrait[]{new HumiditySettingTrait()}; return new DeviceTrait[]{new SensorStateTrait(), new HumiditySettingTrait()};
case "se.hal.struct.devicedata.TemperatureSensorData": case "se.hal.struct.devicedata.TemperatureSensorData":
return new DeviceTrait[]{new TemperatureControlTrait()}; return new DeviceTrait[]{new SensorStateTrait(), new TemperatureControlTrait()};
default: default:
throw new IllegalArgumentException("Unregistered Sensor device data: " + device.getDeviceData()); throw new IllegalArgumentException("Unregistered Sensor device data: " + device.getDeviceData());

View file

@ -31,6 +31,9 @@ import se.hal.struct.devicedata.HumiditySensorData;
import java.util.HashMap; import java.util.HashMap;
/**
* https://developers.google.com/assistant/smarthome/traits/humiditysetting
*/
public class HumiditySettingTrait extends DeviceTrait { public class HumiditySettingTrait extends DeviceTrait {
@Override @Override

View file

@ -0,0 +1,82 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Ziver Koc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.assistant.google.trait;
import com.google.actions.api.smarthome.ExecuteRequest;
import se.hal.EventControllerManager;
import se.hal.intf.HalAbstractDevice;
import se.hal.intf.HalDeviceConfig;
import se.hal.intf.HalDeviceData;
import se.hal.struct.Event;
import se.hal.struct.devicedata.OnOffEventData;
import se.hal.struct.devicedata.TemperatureSensorData;
import java.util.HashMap;
/**
* https://developers.google.com/assistant/smarthome/traits/onoff
*/
public class OnOffTrait extends DeviceTrait {
@Override
String getId() {
return "action.devices.traits.OnOff";
}
@Override
public HashMap<String, Object> generateSyncResponse(HalDeviceConfig config) {
HashMap<String, Object> response = new HashMap<>();
response.put("commandOnlyOnOff", false);
response.put("queryOnlyOnOff", false);
return response;
}
@Override
public HashMap<String, Object> generateQueryResponse(HalDeviceData data) {
HashMap<String, Object> response = new HashMap<>();
if (data instanceof OnOffEventData) {
response.put("on", ((OnOffEventData) data).isOn());
}
return response;
}
@Override
public void execute(HalAbstractDevice device, ExecuteRequest.Inputs.Payload.Commands.Execution execution) {
if ("action.devices.commands.OnOff".equals(execution.command)) {
OnOffEventData eventData = new OnOffEventData();
if ("on".equals(execution.getParams().get("on")))
eventData.turnOn();
else
eventData.turnOff();
device.setDeviceData(eventData);
EventControllerManager.getInstance().send((Event) device);
}
}
}

View file

@ -0,0 +1,63 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Ziver Koc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.assistant.google.trait;
import se.hal.intf.HalDeviceConfig;
import se.hal.intf.HalDeviceData;
import se.hal.struct.devicedata.OnOffEventData;
import se.hal.struct.devicedata.OpenClosedEventData;
import java.util.HashMap;
/**
* https://developers.google.com/assistant/smarthome/traits/openclose
*/
public class OpenCloseTrait extends DeviceTrait {
@Override
String getId() {
return "action.devices.traits.OpenClose";
}
@Override
public HashMap<String, Object> generateSyncResponse(HalDeviceConfig config) {
HashMap<String, Object> response = new HashMap<>();
response.put("queryOnlyOnOff", true);
return response;
}
@Override
public HashMap<String, Object> generateQueryResponse(HalDeviceData data) {
HashMap<String, Object> response = new HashMap<>();
if (data instanceof OpenClosedEventData) {
response.put("openPercent", ((OpenClosedEventData) data).isOpen() ? 100 : 0);
}
return response;
}
}

View file

@ -0,0 +1,74 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Ziver Koc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.assistant.google.trait;
import se.hal.intf.HalDeviceConfig;
import se.hal.intf.HalDeviceData;
import se.hal.struct.devicedata.OpenClosedEventData;
import java.util.ArrayList;
import java.util.HashMap;
/**
* https://developers.google.com/assistant/smarthome/traits/openclose
*/
public class SensorStateTrait extends DeviceTrait {
@Override
String getId() {
return "action.devices.traits.SensorState";
}
@Override
public HashMap<String, Object> generateSyncResponse(HalDeviceConfig config) {
HashMap<String, Object> response = new HashMap<>();
ArrayList<HashMap> sensorStatesSupported = new ArrayList<>();
/*sensorStatesSupported.add(new HashMap<String, Object>() {{
put("name", xxx);
put("maxThresholdCelsius", 60);
}});*/
response.put("sensorStatesSupported", sensorStatesSupported);
return response;
}
@Override
public HashMap<String, Object> generateQueryResponse(HalDeviceData data) {
HashMap<String, Object> response = new HashMap<>();
ArrayList<HashMap> currentSensorStateData = new ArrayList<>();
/*currentSensorStateData.add(new HashMap<String, Object>() {{
put("name", xxx);
put("currentSensorState", xxx);
put("rawValue", xxx);
}});*/
response.put("currentSensorStateData", currentSensorStateData);
return response;
}
}

View file

@ -32,6 +32,9 @@ import se.hal.struct.devicedata.TemperatureSensorData;
import java.util.HashMap; import java.util.HashMap;
/**
* https://developers.google.com/assistant/smarthome/traits/temperaturecontrol
*/
public class TemperatureControlTrait extends DeviceTrait { public class TemperatureControlTrait extends DeviceTrait {
@Override @Override