├── .npmignore ├── .eslintignore ├── src ├── Constants.ts ├── index.ts ├── UrlTemplates.ts ├── AuthenticatedFetcher.ts ├── Vocabularies.ts ├── QuadToHueConverter.ts ├── HueToQuadConverter.ts └── PhilipsHueStore.ts ├── .componentsignore ├── .gitignore ├── .huskyrc.json ├── types └── urijs │ └── index.d.ts ├── tsconfig.json ├── settings-example.json ├── LICENSE ├── .eslintrc.js ├── README.md ├── package.json └── config └── config-hue.json /.npmignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /src/Constants.ts: -------------------------------------------------------------------------------- 1 | export const HueContentType = 'internal/vnd.philips.hue+json'; 2 | -------------------------------------------------------------------------------- /.componentsignore: -------------------------------------------------------------------------------- 1 | [ 2 | "BaseResourceStore", 3 | "TypedRepresentationConverter" 4 | ] 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .eslintcache 2 | dist 3 | node_modules 4 | componentsjs-error-state.json 5 | settings.json 6 | -------------------------------------------------------------------------------- /.huskyrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "npm run build:ts && npm run lint" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AuthenticatedFetcher'; 2 | export * from './HueToQuadConverter'; 3 | export * from './PhilipsHueStore'; 4 | export * from './QuadToHueConverter'; 5 | export * from './UrlTemplates'; 6 | -------------------------------------------------------------------------------- /types/urijs/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'urijs/src/URITemplate' { 2 | // eslint-disable-next-line @typescript-eslint/naming-convention 3 | class URITemplate { 4 | public constructor(...any); 5 | 6 | public expand(fields?: Record): string; 7 | } 8 | export = URITemplate; 9 | } 10 | -------------------------------------------------------------------------------- /src/UrlTemplates.ts: -------------------------------------------------------------------------------- 1 | import URITemplate from 'urijs/src/URITemplate'; 2 | 3 | export const createUrlTemplates = >(patterns: T): 4 | Record => { 5 | const templates: Record = {} as any; 6 | for (const key in patterns) { 7 | if (Object.prototype.hasOwnProperty.call(patterns, key)) { 8 | templates[key] = new URITemplate(patterns[key]); 9 | } 10 | } 11 | return templates; 12 | }; 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node14/tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "incremental": true, 6 | "inlineSources": true, 7 | "newLine": "lf", 8 | "outDir": "dist", 9 | "preserveConstEnums": true, 10 | "sourceMap": true, 11 | "stripInternal": true, 12 | "typeRoots": [ 13 | "types", 14 | "node_modules/@types" 15 | ] 16 | }, 17 | "include": [ 18 | "src" 19 | ], 20 | "exclude": [ 21 | "node_modules" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /settings-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://linkedsoftwaredependencies.org/bundles/npm/solid-hue/^1.0.0/components/context.jsonld", 4 | { "solid-hue": "urn:solid-hue:default:" } 5 | ], 6 | "import": [ 7 | "files-sh:config/config-hue.json" 8 | ], 9 | "@graph": [ 10 | { 11 | "@id": "solid-hue:PhilipsHueStore", 12 | "PhilipsHueStore:_options_username": "" 13 | }, 14 | { 15 | "@id": "solid-hue:AuthenticatedFetcher", 16 | "AuthenticatedFetcher:_accessToken": "" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/AuthenticatedFetcher.ts: -------------------------------------------------------------------------------- 1 | import fetch, { Headers } from 'node-fetch'; 2 | import type { RequestInit, Response } from 'node-fetch'; 3 | 4 | /** 5 | * Authenticated fetcher for the Philips Hue remote API 6 | * (https://developers.meethue.com/develop/hue-api/remote-authentication/) 7 | */ 8 | export class AuthenticatedFetcher { 9 | private readonly accessToken: string; 10 | 11 | public constructor(accessToken: string) { 12 | this.accessToken = accessToken; 13 | } 14 | 15 | public async fetch(url: string, init: RequestInit = {}): Promise { 16 | const headers = new Headers(init.headers); 17 | headers.set('Authorization', await this.getAuthorizationHeader()); 18 | return fetch(url, { ...init, headers }); 19 | } 20 | 21 | public async getAuthorizationHeader(): Promise { 22 | return `Bearer ${this.accessToken}`; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2020–2021 Ruben Verborgh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 19 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 20 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/Vocabularies.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention, function-paren-newline */ 2 | import { createUriAndTermNamespace } from '@solid/community-server'; 3 | 4 | export const DOGONT = createUriAndTermNamespace('http://elite.polito.it/ontologies/dogont.owl#', 5 | 'Lamp', 6 | ); 7 | 8 | export const HUE = createUriAndTermNamespace('urn:tmp:philips:hue#', 9 | 'brightness', 10 | 'hue', 11 | 'on', 12 | 'saturation', 13 | ); 14 | 15 | export const RDFS = createUriAndTermNamespace('http://www.w3.org/2000/01/rdf-schema#', 16 | 'label', 17 | ); 18 | 19 | export const XSD = createUriAndTermNamespace('http://www.w3.org/2001/XMLSchema#', 20 | 'boolean', 21 | 'integer', 22 | ); 23 | 24 | export const stateMapping = { 25 | [HUE.on]: { field: 'on', dataType: XSD.terms.boolean }, 26 | [HUE.hue]: { field: 'hue', dataType: XSD.terms.integer }, 27 | [HUE.saturation]: { field: 'sat', dataType: XSD.terms.integer }, 28 | [HUE.brightness]: { field: 'bri', dataType: XSD.terms.integer }, 29 | }; 30 | 31 | export const valueParsers = { 32 | [XSD.integer]: Number, 33 | [XSD.boolean]: (value: string): boolean => !/^(?:0+|false)$/iu.test(value), 34 | }; 35 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | parserOptions: { 5 | tsconfigRootDir: __dirname, 6 | project: ['./tsconfig.json'], 7 | }, 8 | plugins: [ 9 | 'tsdoc', 10 | 'import', 11 | 'unused-imports', 12 | ], 13 | extends: [ 14 | 'es/node', 15 | 'eslint:recommended', 16 | 'plugin:import/errors', 17 | 'plugin:import/warnings', 18 | 'plugin:import/typescript', 19 | ], 20 | rules: { 21 | '@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }], 22 | '@typescript-eslint/lines-between-class-members': ['error', { exceptAfterSingleLine: true }], 23 | '@typescript-eslint/no-unused-vars': ['error', { args: 'none' }], 24 | '@typescript-eslint/no-unnecessary-condition': 'off', 25 | '@typescript-eslint/space-before-function-paren': ['error', 'never'], 26 | 'array-bracket-spacing': ['error', 'never'], 27 | 'class-methods-use-this': 'off', 28 | 'comma-dangle': ['error', 'always-multiline'], 29 | 'import/no-unresolved': 'off', 30 | 'lines-around-comment': 'off', 31 | 'no-duplicate-imports': 'off', 32 | 'no-param-reassign': 'off', 33 | 'no-unused-vars': ['error', { args: 'none' }], 34 | 'padding-line-between-statements': 'off', 35 | 'quote-props': ['error', 'consistent-as-needed'], 36 | 'sort-imports': 'off', 37 | 'strict': 'off', 38 | }, 39 | overrides: [ 40 | { 41 | files: '*.js', 42 | parser: 'espree', 43 | }, 44 | ], 45 | }; 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Philips Hue module for the Community Solid Server 2 | [![npm version](https://badge.fury.io/js/solid-hue.svg)](https://www.npmjs.com/package/solid-hue) 3 | 4 | 💡 Control your [Philips Hue Lights](https://www.philips-hue.com/) 5 | from within [Solid](https://solidproject.org/) applications. 6 | 7 | 8 | ## What it is 9 | - The [Community Solid Server](https://github.com/solid/community-server/) 10 | allows people to expose their data on the Web behind access control. 11 | - This `philips-hue` npm package provides a plug-in store for the Community Solid Server 12 | that exposes your Philips Hue lights as read/write documents. 13 | - Solid apps can thereby access your light setup 14 | as if they would access any other document. 15 | 16 | 17 | ## How to install 18 | From the npm package registry: 19 | ```shell 20 | mkdir my-server 21 | cd my-server 22 | npm install @solid/community-server@v0.5.0 solid-hue 23 | ``` 24 | 25 | As a developer: 26 | ```shell 27 | git clone git@github.com:RubenVerborgh/solid-hue.git 28 | cd solid-hue 29 | npm ci 30 | ``` 31 | 32 | ## How to configure 33 | Create a file `settings.json` 34 | from [this template](https://github.com/RubenVerborgh/solid-hue/blob/main/settings-example.json), 35 | and filling out your settings. 36 | 37 | You can obtain your settings 38 | by [creating a new Philips Hue App](https://developers.meethue.com/my-apps/) 39 | and following the [set-up steps](https://developers.meethue.com/develop/hue-api/remote-api-quick-start-guide/). 40 | 41 | 42 | ## How to run 43 | Execute the following command: 44 | ```shell 45 | npx community-solid-server -c settings.json -m . 46 | ``` 47 | 48 | Now you can access your lights 49 | from http://localhost:3000/home/lights 50 | 51 | 52 | ## License 53 | 54 | ©2020–2021 Ruben Verborgh, [MIT License](https://github.com/RubenVerborgh/philips-hue/blob/master/LICENSE.md) 55 | -------------------------------------------------------------------------------- /src/QuadToHueConverter.ts: -------------------------------------------------------------------------------- 1 | import type { Literal, Quad } from 'rdf-js'; 2 | import { 3 | INTERNAL_QUADS, 4 | BasicRepresentation, 5 | TypedRepresentationConverter, 6 | transformSafely, 7 | } from '@solid/community-server'; 8 | import type { Representation, RepresentationConverterArgs } from '@solid/community-server'; 9 | import { stateMapping, valueParsers } from './Vocabularies'; 10 | import { HueContentType } from './Constants'; 11 | 12 | /** 13 | * Converts quads to the Philips Hue JSON format. 14 | */ 15 | export class QuadToHueConverter extends TypedRepresentationConverter { 16 | public constructor() { 17 | super(INTERNAL_QUADS, HueContentType); 18 | } 19 | 20 | public async handle({ identifier, representation }: RepresentationConverterArgs): Promise { 21 | const lights: Record = {}; 22 | const data = transformSafely(representation.data, { 23 | writableObjectMode: true, 24 | // Convert every triple to a property about a light 25 | transform({ subject: { value: subject }, predicate: { value: predicate }, object }): void { 26 | // Extract the ID of the light 27 | const light = subject.startsWith(identifier.path) ? subject.slice(identifier.path.length + 1) : ''; 28 | 29 | // Extract the property value if it occurs in the mapping 30 | const mapping = stateMapping[predicate]; 31 | if (/^\d+$/u.test(light) && mapping?.dataType.equals((object as Literal).datatype)) { 32 | // Initialize the state of the light if none was present yet 33 | if (!(light in lights)) { 34 | lights[light] = { state: {}}; 35 | } 36 | // Add the parsed object to the light's state 37 | const parser = valueParsers[mapping.dataType.value]; 38 | lights[light].state[mapping.field] = parser(object.value); 39 | } 40 | }, 41 | // Transforms the end result to JSON 42 | flush(): void { 43 | this.push(JSON.stringify(lights)); 44 | }, 45 | }); 46 | return new BasicRepresentation(data, representation.metadata, HueContentType); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/HueToQuadConverter.ts: -------------------------------------------------------------------------------- 1 | import { namedNode, literal } from '@rdfjs/data-model'; 2 | import { 3 | INTERNAL_QUADS, PREFERRED_PREFIX_TERM, RDF, 4 | BasicRepresentation, 5 | RepresentationMetadata, 6 | TypedRepresentationConverter, 7 | pushQuad, 8 | transformSafely, 9 | } from '@solid/community-server'; 10 | import type { Representation, RepresentationConverterArgs } from '@solid/community-server'; 11 | import { DOGONT, HUE, RDFS, stateMapping } from './Vocabularies'; 12 | import { HueContentType } from './Constants'; 13 | 14 | /** 15 | * Converts the Philips Hue JSON format to quads. 16 | */ 17 | export class HueToQuadConverter extends TypedRepresentationConverter { 18 | public constructor() { 19 | super(HueContentType, INTERNAL_QUADS); 20 | } 21 | 22 | public async handle({ identifier, representation }: RepresentationConverterArgs): Promise { 23 | const metadata = new RepresentationMetadata(representation.metadata, INTERNAL_QUADS); 24 | metadata.addQuad(DOGONT.terms.namespace, PREFERRED_PREFIX_TERM, 'dogont'); 25 | metadata.addQuad(RDFS.terms.namespace, PREFERRED_PREFIX_TERM, 'rdfs'); 26 | metadata.addQuad(HUE.terms.namespace, PREFERRED_PREFIX_TERM, 'ph'); 27 | 28 | // Transform the JSON into RDF quads 29 | const json: string[] = []; 30 | const data = transformSafely(representation.data, { 31 | readableObjectMode: true, 32 | transform: (buffer): any => json.push(buffer), 33 | flush(): void { 34 | // Generate RDF quads for every light 35 | const lights: Record = JSON.parse(json.join('')); 36 | for (const [key, light] of Object.entries(lights)) { 37 | // Generate the type and label 38 | const id = namedNode(`${identifier.path}#${encodeURIComponent(key)}`); 39 | pushQuad(this, id, RDF.terms.type, DOGONT.terms.Lamp); 40 | pushQuad(this, id, RDFS.terms.label, literal(light.name)); 41 | // Generate state properties 42 | for (const [predicate, { field, dataType }] of Object.entries(stateMapping)) { 43 | pushQuad(this, id, predicate, literal(`${light.state[field]}`, dataType)); 44 | } 45 | } 46 | }, 47 | }); 48 | return new BasicRepresentation(data, metadata); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solid-hue", 3 | "version": "1.0.2", 4 | "description": "A Community Solid Server backend for Philips Hue", 5 | "keywords": [ 6 | "solid", 7 | "linked-data", 8 | "rdf", 9 | "ldp" 10 | ], 11 | "license": "MIT", 12 | "author": "Ruben Verborgh (https://ruben.verborgh.org/)", 13 | "main": "./dist/index.js", 14 | "types": "./dist/index.d.ts", 15 | "repository": "git@github.com:RubenVerborgh/solid-hue.git", 16 | "bugs": { 17 | "url": "https://github.com/RubenVerborgh/solid-hue/issues" 18 | }, 19 | "homepage": "https://github.com/RubenVerborgh/solid-hue#readme", 20 | "files": [ 21 | "config", 22 | "dist" 23 | ], 24 | "lsd:module": "https://linkedsoftwaredependencies.org/bundles/npm/solid-hue", 25 | "lsd:components": "dist/components/components.jsonld", 26 | "lsd:contexts": { 27 | "https://linkedsoftwaredependencies.org/bundles/npm/solid-hue/^1.0.0/components/context.jsonld": "dist/components/context.jsonld" 28 | }, 29 | "lsd:importPaths": { 30 | "https://linkedsoftwaredependencies.org/bundles/npm/solid-hue/^1.0.0/components/": "dist/components/", 31 | "https://linkedsoftwaredependencies.org/bundles/npm/solid-hue/^1.0.0/config/": "config/", 32 | "https://linkedsoftwaredependencies.org/bundles/npm/solid-hue/^1.0.0/dist/": "dist/" 33 | }, 34 | "scripts": { 35 | "build": "npm run build:ts && npm run build:components", 36 | "build:components": "componentsjs-generator -s src -c dist/components -i .componentsignore", 37 | "build:ts": "tsc", 38 | "lint": "eslint . --cache", 39 | "prepare": "npm run build" 40 | }, 41 | "dependencies": { 42 | "@rdfjs/data-model": "^1.2.0", 43 | "@types/node-fetch": "^2.5.7", 44 | "@types/rdf-js": "^4.0.0", 45 | "node-fetch": "^2.6.1", 46 | "urijs": "^1.19.5" 47 | }, 48 | "peerDependencies": { 49 | "@solid/community-server": "^0.8.1" 50 | }, 51 | "devDependencies": { 52 | "@solid/community-server": "^0.8.1", 53 | "@tsconfig/node14": "^1.0.0", 54 | "@typescript-eslint/eslint-plugin": "^4.11.1", 55 | "@typescript-eslint/parser": "^4.11.1", 56 | "componentsjs-generator": "^2.0.0", 57 | "eslint": "^7.16.0", 58 | "eslint-config-es": "^3.26.13", 59 | "eslint-plugin-import": "^2.22.1", 60 | "eslint-plugin-tsdoc": "^0.2.10", 61 | "eslint-plugin-unused-imports": "^1.0.1", 62 | "husky": "^4.3.6", 63 | "typescript": "^4.1.3" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/PhilipsHueStore.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { 3 | getLoggerFor, 4 | readableToString, 5 | BaseResourceStore, 6 | BasicRepresentation, 7 | UnsupportedMediaTypeHttpError, 8 | } from '@solid/community-server'; 9 | import type { 10 | Patch, 11 | Representation, 12 | ResourceIdentifier, 13 | } from '@solid/community-server'; 14 | import type { AuthenticatedFetcher } from './AuthenticatedFetcher'; 15 | import { HueContentType } from './Constants'; 16 | import { createUrlTemplates } from './UrlTemplates'; 17 | 18 | // Entry points of the Philips Hue Remote API 19 | const templates = createUrlTemplates({ 20 | lights: 'https://api.meethue.com/bridge/{username}/lights', 21 | light: 'https://api.meethue.com/bridge/{username}/lights/{light}/state', 22 | }); 23 | 24 | /** 25 | * Store that provides access to a set of Philips Hue lights 26 | * in the Philips Hue JSON format 27 | * (https://developers.meethue.com/develop/hue-api/lights-api/). 28 | */ 29 | export class PhilipsHueStore extends BaseResourceStore { 30 | private readonly fetcher: AuthenticatedFetcher; 31 | private readonly settings: Record; 32 | private readonly logger = getLoggerFor(this); 33 | 34 | public constructor(options: { 35 | fetcher: AuthenticatedFetcher; 36 | username: string; 37 | }) { 38 | super(); 39 | const { fetcher, ...settings } = options; 40 | this.fetcher = fetcher; 41 | this.settings = settings; 42 | } 43 | 44 | /** 45 | * Retrieves a JSON representation of all lights. 46 | */ 47 | public async getRepresentation(identifier: ResourceIdentifier): Promise { 48 | const url = templates.lights.expand(this.settings); 49 | this.logger.debug(`Retrieving light status from ${url}`); 50 | 51 | const response = await this.fetcher.fetch(url); 52 | assert.equal(response.status, 200); 53 | 54 | return new BasicRepresentation(await response.text(), identifier, HueContentType); 55 | } 56 | 57 | /** 58 | * Replaces the state of multiple lights via a JSON representation. 59 | */ 60 | public async setRepresentation(identifier: ResourceIdentifier, representation: Representation): Promise { 61 | // Technically, PUT is for replacing entire representations, and PATCH for (partial) updates. 62 | // However, any partial or incomplete resource state that could arrive via PUT 63 | // is complemented by the current physical state of the lights, so we just treat it as a PATCH. 64 | return this.modifyResource(identifier, representation); 65 | } 66 | 67 | /** 68 | * Adjusts the state of multiple lights via a JSON representation. 69 | */ 70 | public async modifyResource(identifier: ResourceIdentifier, patch: Patch): Promise { 71 | if (patch.metadata.contentType !== HueContentType) { 72 | throw new UnsupportedMediaTypeHttpError(`Only ${HueContentType} is supported`); 73 | } 74 | 75 | // Each light in the patch needs to be updated separately 76 | const lights = JSON.parse(await readableToString(patch.data)); 77 | const updates = Object.keys(lights).map(async(light): Promise => 78 | this.setLightState(light, lights[light].state || {})); 79 | await Promise.all(updates); 80 | 81 | return [identifier]; 82 | } 83 | 84 | /** 85 | * Updates the state of an individual light. 86 | */ 87 | protected async setLightState(light: string, state: Record): Promise { 88 | const url = templates.light.expand({ ...this.settings, light }); 89 | this.logger.debug(`Updating light status of ${url}`, { state }); 90 | 91 | const response = await this.fetcher.fetch(url, { 92 | method: 'PUT', 93 | headers: { 'content-type': 'application/json' }, 94 | body: JSON.stringify(state), 95 | }); 96 | assert.equal(response.status, 200); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /config/config-hue.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://linkedsoftwaredependencies.org/bundles/npm/solid-hue/^1.0.0/components/context.jsonld", 4 | "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld", 5 | { 6 | "solid-hue": "urn:solid-hue:default:", 7 | "solid-server": "urn:solid-server:default:" 8 | } 9 | ], 10 | "import": [ 11 | "files-scs:config/presets/acl.json", 12 | "files-scs:config/presets/http.json", 13 | "files-scs:config/presets/identifiers/suffix-identifiers.json", 14 | "files-scs:config/presets/init.json", 15 | "files-scs:config/presets/ldp.json", 16 | "files-scs:config/presets/ldp/credentials-extractor.json", 17 | "files-scs:config/presets/ldp/metadata-handler.json", 18 | "files-scs:config/presets/ldp/operation-handler.json", 19 | "files-scs:config/presets/ldp/permissions-extractor.json", 20 | "files-scs:config/presets/ldp/response-writer.json", 21 | "files-scs:config/presets/ldp/request-parser.json", 22 | "files-scs:config/presets/ldp/websockets.json", 23 | "files-scs:config/presets/logging.json", 24 | "files-scs:config/presets/middleware.json", 25 | "files-scs:config/presets/pod-management.json", 26 | "files-scs:config/presets/representation-conversion.json", 27 | "files-scs:config/presets/static.json", 28 | "files-scs:config/presets/storage/backend/storage-memory.json", 29 | "files-scs:config/presets/storage/routing/regex-routing.json", 30 | "files-scs:config/presets/storage-wrapper.json", 31 | "files-scs:config/presets/cli-params.json" 32 | ], 33 | "@graph": [ 34 | { 35 | "@id": "solid-server:RegexRouterRule", 36 | "RegexRouterRule:_storeMap": [ 37 | { 38 | "RegexRouterRule:_storeMap_key": "^/home/lights$", 39 | "RegexRouterRule:_storeMap_value": { "@id": "solid-hue:RdfEnabledPhilipsHueStore" } 40 | }, 41 | { 42 | "RegexRouterRule:_storeMap_key": ".*", 43 | "RegexRouterRule:_storeMap_value": { "@id": "solid-server:MemoryResourceStore" } 44 | } 45 | ] 46 | }, 47 | { 48 | "@id": "solid-hue:PhilipsHueStore", 49 | "@type": "PhilipsHueStore", 50 | "PhilipsHueStore:_options_fetcher": { "@id": "solid-hue:AuthenticatedFetcher" } 51 | }, 52 | { 53 | "@id": "solid-hue:AuthenticatedFetcher", 54 | "@type": "AuthenticatedFetcher" 55 | }, 56 | { 57 | "@id": "solid-hue:RdfEnabledPhilipsHueStore", 58 | "@type": "RepresentationConvertingStore", 59 | "RepresentationConvertingStore:_source": { 60 | "@id": "solid-hue:PhilipsHueStore" 61 | }, 62 | "RepresentationConvertingStore:_options_outConverter": { 63 | "@id": "solid-hue:RepresentationConverter" 64 | }, 65 | "RepresentationConvertingStore:_options_inConverter": { 66 | "@id": "solid-hue:RepresentationConverter" 67 | }, 68 | "RepresentationConvertingStore:_options_inType": "internal/vnd.philips.hue+json" 69 | }, 70 | { 71 | "@id": "solid-hue:RepresentationConverter", 72 | "@type": "WaterfallHandler", 73 | "WaterfallHandler:_handlers": [ 74 | { "@type": "HueToQuadConverter" }, 75 | { "@type": "QuadToHueConverter" }, 76 | { 77 | "@id": "solid-hue:HueToRdfConverter", 78 | "@type": "ChainedConverter", 79 | "ChainedConverter:_converters": [ 80 | { "@type": "HueToQuadConverter" }, 81 | { "@id": "solid-server:QuadToRdfConverter" } 82 | ] 83 | }, 84 | { 85 | "@id": "solid-hue:RdfToHueConverter", 86 | "@type": "ChainedConverter", 87 | "ChainedConverter:_converters": [ 88 | { "@id": "solid-server:RdfToQuadConverter" }, 89 | { "@type": "QuadToHueConverter" } 90 | ] 91 | } 92 | ] 93 | } 94 | ] 95 | } 96 | --------------------------------------------------------------------------------