Explorar o código

Add REST route for device aliases

Christopher Mullins %!s(int64=6) %!d(string=hai) anos
pai
achega
73308361a9

+ 81 - 13
lib/WebServer/MiLightHttpServer.cpp

@@ -47,6 +47,13 @@ void MiLightHttpServer::begin() {
     .on(HTTP_GET, std::bind(&MiLightHttpServer::handleGetGroup, this, _1));
 
   server
+    .buildHandler("/gateways/:device_alias")
+    .on(HTTP_PUT, std::bind(&MiLightHttpServer::handleUpdateGroupAlias, this, _1))
+    .on(HTTP_POST, std::bind(&MiLightHttpServer::handleUpdateGroupAlias, this, _1))
+    .on(HTTP_DELETE, std::bind(&MiLightHttpServer::handleDeleteGroupAlias, this, _1))
+    .on(HTTP_GET, std::bind(&MiLightHttpServer::handleGetGroupAlias, this, _1));
+
+  server
     .buildHandler("/raw_commands/:type")
     .on(HTTP_ANY, std::bind(&MiLightHttpServer::handleSendRaw, this, _1));
 
@@ -311,6 +318,12 @@ void MiLightHttpServer::handleListenGateway(RequestContext& request) {
 }
 
 void MiLightHttpServer::sendGroupState(BulbId& bulbId, GroupState* state, RichHttp::Response& response) {
+  // Wait for packet queue to flush out.  State will not have been updated before that.
+  // Bit hacky to call loop outside of main loop, but should be fine.
+  while (packetSender->isSending()) {
+    packetSender->loop();
+  }
+
   JsonObject obj = response.json.to<JsonObject>();
 
   if (state != NULL) {
@@ -319,6 +332,24 @@ void MiLightHttpServer::sendGroupState(BulbId& bulbId, GroupState* state, RichHt
   }
 }
 
+void MiLightHttpServer::_handleGetGroup(BulbId bulbId, RequestContext& request) {
+  sendGroupState(bulbId, stateStore->get(bulbId), request.response);
+}
+
+void MiLightHttpServer::handleGetGroupAlias(RequestContext& request) {
+  const String alias = request.pathVariables.get("device_alias");
+
+  std::map<String, BulbId>::iterator it = settings.groupIdAliases.find(alias);
+
+  if (it == settings.groupIdAliases.end()) {
+    request.response.setCode(404);
+    request.response.json[F("error")] = F("Device alias not found");
+    return;
+  }
+
+  _handleGetGroup(it->second, request);
+}
+
 void MiLightHttpServer::handleGetGroup(RequestContext& request) {
   const String _deviceId = request.pathVariables.get("device_id");
   uint8_t _groupId = atoi(request.pathVariables.get("group_id"));
@@ -333,7 +364,7 @@ void MiLightHttpServer::handleGetGroup(RequestContext& request) {
   }
 
   BulbId bulbId(parseInt<uint16_t>(_deviceId), _groupId, _remoteType->type);
-  sendGroupState(bulbId, stateStore->get(bulbId), request.response);
+  _handleGetGroup(bulbId, request);
 }
 
 void MiLightHttpServer::handleDeleteGroup(RequestContext& request) {
@@ -350,6 +381,24 @@ void MiLightHttpServer::handleDeleteGroup(RequestContext& request) {
   }
 
   BulbId bulbId(parseInt<uint16_t>(_deviceId), _groupId, _remoteType->type);
+  _handleDeleteGroup(bulbId, request);
+}
+
+void MiLightHttpServer::handleDeleteGroupAlias(RequestContext& request) {
+  const String alias = request.pathVariables.get("device_alias");
+
+  std::map<String, BulbId>::iterator it = settings.groupIdAliases.find(alias);
+
+  if (it == settings.groupIdAliases.end()) {
+    request.response.setCode(404);
+    request.response.json[F("error")] = F("Device alias not found");
+    return;
+  }
+
+  _handleDeleteGroup(it->second, request);
+}
+
+void MiLightHttpServer::_handleDeleteGroup(BulbId bulbId, RequestContext& request) {
   stateStore->clear(bulbId);
 
   if (groupDeletedHandler != NULL) {
@@ -359,13 +408,36 @@ void MiLightHttpServer::handleDeleteGroup(RequestContext& request) {
   request.response.json["success"] = true;
 }
 
+void MiLightHttpServer::handleUpdateGroupAlias(RequestContext& request) {
+  const String alias = request.pathVariables.get("device_alias");
+
+  std::map<String, BulbId>::iterator it = settings.groupIdAliases.find(alias);
+
+  if (it == settings.groupIdAliases.end()) {
+    request.response.setCode(404);
+    request.response.json[F("error")] = F("Device alias not found");
+    return;
+  }
+
+  BulbId& bulbId = it->second;
+  const MiLightRemoteConfig* config = MiLightRemoteConfig::fromType(bulbId.deviceType);
+
+  if (config == NULL) {
+    char buffer[40];
+    sprintf_P(buffer, PSTR("Unknown device type: %s"), bulbId.deviceType);
+    request.response.setCode(400);
+    request.response.json["error"] = buffer;
+    return;
+  }
+
+  milightClient->prepare(config, bulbId.deviceId, bulbId.groupId);
+  handleRequest(request.getJsonBody().as<JsonObject>());
+  sendGroupState(bulbId, stateStore->get(bulbId), request.response);
+}
+
 void MiLightHttpServer::handleUpdateGroup(RequestContext& request) {
   JsonObject reqObj = request.getJsonBody().as<JsonObject>();
 
-  milightClient->setRepeatsOverride(
-    settings.httpRepeatFactor * settings.packetRepeats
-  );
-
   String _deviceIds = request.pathVariables.get("device_id");
   String _groupIds = request.pathVariables.get("group_id");
   String _remoteTypes = request.pathVariables.get("type");
@@ -411,15 +483,7 @@ void MiLightHttpServer::handleUpdateGroup(RequestContext& request) {
     }
   }
 
-  milightClient->clearRepeatsOverride();
-
   if (groupCount == 1) {
-    // Wait for packet queue to flush out.  State will not have been updated before that.
-    // Bit hacky to call loop outside of main loop, but should be fine.
-    while (packetSender->isSending()) {
-      packetSender->loop();
-    }
-
     sendGroupState(foundBulbId, stateStore->get(foundBulbId), request.response);
   } else {
     request.response.json["success"] = true;
@@ -427,7 +491,11 @@ void MiLightHttpServer::handleUpdateGroup(RequestContext& request) {
 }
 
 void MiLightHttpServer::handleRequest(const JsonObject& request) {
+  milightClient->setRepeatsOverride(
+    settings.httpRepeatFactor * settings.packetRepeats
+  );
   milightClient->update(request);
+  milightClient->clearRepeatsOverride();
 }
 
 void MiLightHttpServer::handleSendRaw(RequestContext& request) {

+ 9 - 1
lib/WebServer/MiLightHttpServer.h

@@ -67,9 +67,17 @@ protected:
   void handleFirmwarePost();
   void handleListenGateway(RequestContext& request);
   void handleSendRaw(RequestContext& request);
+
   void handleUpdateGroup(RequestContext& request);
-  void handleDeleteGroup(RequestContext& request);
+  void handleUpdateGroupAlias(RequestContext& request);
+
   void handleGetGroup(RequestContext& request);
+  void handleGetGroupAlias(RequestContext& request);
+  void _handleGetGroup(BulbId bulbId, RequestContext& request);
+
+  void handleDeleteGroup(RequestContext& request);
+  void handleDeleteGroupAlias(RequestContext& request);
+  void _handleDeleteGroup(BulbId bulbId, RequestContext& request);
 
   void handleRequest(const JsonObject& request);
   void handleWsEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length);

+ 45 - 0
test/remote/spec/rest_spec.rb

@@ -110,4 +110,49 @@ RSpec.describe 'REST Server' do
       expect(state['status']).to eq('ON')
     end
   end
+
+  context 'device aliases' do
+    before(:all) do
+      @device_id = {
+        id: @client.generate_id,
+        type: 'rgb_cct',
+        group_id: 1
+      }
+      @alias = 'test'
+
+      @client.patch_settings(
+        group_id_aliases: {
+          @alias => [
+            @device_id[:type],
+            @device_id[:id],
+            @device_id[:group_id]
+          ]
+        }
+      )
+
+      @client.delete_state(@device_id)
+    end
+
+    it 'should respond with a 404 for an alias that doesn\'t exist' do
+      expect {
+        @client.put("/gateways/__#{@alias}", status: 'on')
+      }.to raise_error(Net::HTTPServerException)
+    end
+
+    it 'should update state for known alias' do
+      path = "/gateways/#{@alias}"
+
+      @client.put(path, status: 'ON', hue: 100)
+      state = @client.get(path)
+
+      expect(state['status']).to eq('ON')
+      expect(state['hue']).to eq(100)
+
+      # ensure state for the non-aliased ID is the same
+      state = @client.get_state(@device_id)
+
+      expect(state['status']).to eq('ON')
+      expect(state['hue']).to eq(100)
+    end
+  end
 end