// +---------------------------------------------------------------------- declare(strict_types=1); namespace addon\iot\controller\api; use think\facade\Db; use addon\iot\service\ShareService; use addon\iot\service\SceneService; use addon\iot\service\GroupService; /** * App 端联动域 API(需框架会员 JWT 登录) * 路由前缀:/iot/api/scene/...(联动域;旧 /iot/api/client/* 兼容别名迁移期保留) * * 覆盖:场景联动(触发器/动作/执行/日志)+ 设备分组(房间/自定义)。 * 两者同属"用户组织设备的方式",合在一个控制器。 */ class Scene extends ClientBase { /* ============================================================ * 场景联动(智慧生活自动化) * ========================================================== */ /** * 我的场景列表 * GET /iot/api/scene/sceneList */ public function sceneList() { $uid = $this->memberId(); if ($uid <= 0) { return $this->result->apiError('未登录'); } $list = SceneService::list($uid); return $this->result->apiSuccess(['list' => $list, 'total' => count($list)]); } /** * 场景详情(含触发器/动作) * GET /iot/api/scene/sceneDetail?id=1 */ public function sceneDetail() { $uid = $this->memberId(); $id = (int) $this->request->get('id', 0); if ($uid <= 0) { return $this->result->apiError('未登录'); } $scene = SceneService::findScene($id); if (!$scene) { return $this->result->apiError('场景不存在'); } if ((int) $scene['member_id'] !== $uid) { return $this->result->apiError('无权查看该场景'); } return $this->result->apiSuccess($scene); } /** * 新建/编辑场景 * POST /iot/api/scene/saveScene * param: id?, title, icon, enabled, triggers(JSON数组), actions(JSON数组) * triggers: [{"type":"device_state|schedule|manual","topic":"/devices/+/state", * "condition_json":[{field,op,value,ref}],"cron":"0 8 * * *","enabled":1}] * actions: [{"type":"send_cmd|http|notify","device_uuid":"...","payload_json":"...","topic":"..."}] */ public function saveScene() { $uid = $this->memberId(); if ($uid <= 0) { return $this->result->apiError('未登录'); } try { $data = $this->request->post(); $data['member_id'] = $uid; $triggers = json_decode((string) ($data['triggers'] ?? ''), true); $actions = json_decode((string) ($data['actions'] ?? ''), true); if (!is_array($triggers) && !is_array($actions)) { return $this->result->apiError('触发器/动作格式错误'); } $data['triggers'] = is_array($triggers) ? $triggers : []; $data['actions'] = is_array($actions) ? $actions : []; $id = SceneService::save($data); } catch (\Throwable $e) { return $this->result->apiError($e->getMessage()); } return $this->result->apiSuccess(['id' => $id]); } /** * 删除场景 * POST /iot/api/scene/deleteScene * param: id */ public function deleteScene() { $uid = $this->memberId(); $id = (int) $this->request->post('id', 0); if ($uid <= 0) { return $this->result->apiError('未登录'); } $scene = SceneService::findScene($id); if (!$scene || (int) $scene['member_id'] !== $uid) { return $this->result->apiError('场景不存在或无权操作'); } SceneService::remove($id, $uid); return $this->result->apiSuccess('已删除'); } /** * 启用/停用场景 * POST /iot/api/scene/toggleScene * param: id, enabled(0/1) */ public function toggleScene() { $uid = $this->memberId(); $id = (int) $this->request->post('id', 0); $enabled = (int) $this->request->post('enabled', 0); if ($uid <= 0) { return $this->result->apiError('未登录'); } $scene = SceneService::findScene($id); if (!$scene || (int) $scene['member_id'] !== $uid) { return $this->result->apiError('场景不存在或无权操作'); } SceneService::toggle($id, $enabled, $uid); return $this->result->apiSuccess('已更新'); } /** * 手动执行场景 * POST /iot/api/scene/runScene * param: id */ public function runScene() { $uid = $this->memberId(); $id = (int) $this->request->post('id', 0); if ($uid <= 0) { return $this->result->apiError('未登录'); } $scene = SceneService::findScene($id); if (!$scene || (int) $scene['member_id'] !== $uid) { return $this->result->apiError('场景不存在或无权操作'); } $ok = SceneService::runScene($id, 'manual', 'App手动执行'); return $this->result->apiSuccess($ok ? '已触发执行' : '执行完成(无动作或场景停用)'); } /** * 场景执行日志 * GET /iot/api/scene/sceneLogs?id=1&page=1 */ public function sceneLogs() { $uid = $this->memberId(); $id = (int) $this->request->get('id', 0); if ($uid <= 0) { return $this->result->apiError('未登录'); } $scene = SceneService::findScene($id); if (!$scene || (int) $scene['member_id'] !== $uid) { return $this->result->apiError('场景不存在或无权操作'); } $page = max(1, (int) $this->request->get('page', 1)); $size = min(100, max(1, (int) $this->request->get('size', 20))); $data = SceneService::logs($id, $page, $size); return $this->result->apiSuccess($data); } /* ============================================================ * 设备分组(智慧生活:房间/自定义) * ========================================================== */ /** * 我的分组列表(含设备数/在线数) * GET /iot/api/scene/groupList */ public function groupList() { $uid = $this->memberId(); if ($uid <= 0) { return $this->result->apiError('未登录'); } $list = GroupService::groups($uid); return $this->result->apiSuccess(['list' => $list, 'total' => count($list)]); } /** * 新建/编辑分组 * POST /iot/api/scene/saveGroup * param: id?, title, type(room/custom), sort? */ public function saveGroup() { $uid = $this->memberId(); if ($uid <= 0) { return $this->result->apiError('未登录'); } try { $data = $this->request->post(); $data['member_id'] = $uid; $id = GroupService::save($data); } catch (\Throwable $e) { return $this->result->apiError($e->getMessage()); } return $this->result->apiSuccess(['id' => $id]); } /** * 删除分组 * POST /iot/api/scene/deleteGroup * param: id */ public function deleteGroup() { $uid = $this->memberId(); $id = (int) $this->request->post('id', 0); if ($uid <= 0) { return $this->result->apiError('未登录'); } try { GroupService::remove($id, $uid); } catch (\Throwable $e) { return $this->result->apiError($e->getMessage()); } return $this->result->apiSuccess('已删除'); } /** * 设备加入分组 * POST /iot/api/scene/groupAddDevice * param: group_id, device_id */ public function groupAddDevice() { $uid = $this->memberId(); $groupId = (int) $this->request->post('group_id', 0); $deviceId = (int) $this->request->post('device_id', 0); if ($uid <= 0) { return $this->result->apiError('未登录'); } // 校验设备归属/共享权限 $dev = Db::name('iot_device')->where('id', $deviceId)->find(); if (!$dev || !ShareService::canAccess((string) $dev['uuid'], $uid, ShareService::PERM_VIEW)) { return $this->result->apiError('设备不存在或无权操作'); } try { GroupService::addDevice($groupId, $deviceId, $uid); } catch (\Throwable $e) { return $this->result->apiError($e->getMessage()); } return $this->result->apiSuccess('已加入分组'); } /** * 设备移出分组 * POST /iot/api/scene/groupRemoveDevice * param: group_id, device_id */ public function groupRemoveDevice() { $uid = $this->memberId(); $groupId = (int) $this->request->post('group_id', 0); $deviceId = (int) $this->request->post('device_id', 0); if ($uid <= 0) { return $this->result->apiError('未登录'); } try { GroupService::removeDevice($groupId, $deviceId, $uid); } catch (\Throwable $e) { return $this->result->apiError($e->getMessage()); } return $this->result->apiSuccess('已移出分组'); } /** * 组内设备列表 * GET /iot/api/scene/groupDevices?group_id=1 */ public function groupDevices() { $uid = $this->memberId(); $groupId = (int) $this->request->get('group_id', 0); if ($uid <= 0) { return $this->result->apiError('未登录'); } try { $data = GroupService::devicesByGroup($groupId, $uid); } catch (\Throwable $e) { return $this->result->apiError($e->getMessage()); } return $this->result->apiSuccess($data); } }