{"id":16624333,"url":"https://github.com/dcramer/panelkit","last_synced_at":"2025-03-21T15:31:11.193Z","repository":{"id":43517328,"uuid":"262924889","full_name":"dcramer/panelkit","owner":"dcramer","description":"WIP: A kit for building a tablet-focused Home Assistant UI.","archived":false,"fork":false,"pushed_at":"2023-01-05T20:13:46.000Z","size":6730,"stargazers_count":9,"open_issues_count":20,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-15T15:17:41.262Z","etag":null,"topics":["home-assistant","homeassistant","tileboard"],"latest_commit_sha":null,"homepage":"http://cra.mr/panelkit/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dcramer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-11T03:01:56.000Z","updated_at":"2023-09-08T18:07:17.000Z","dependencies_parsed_at":"2023-02-04T14:02:26.000Z","dependency_job_id":null,"html_url":"https://github.com/dcramer/panelkit","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcramer%2Fpanelkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcramer%2Fpanelkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcramer%2Fpanelkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcramer%2Fpanelkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dcramer","download_url":"https://codeload.github.com/dcramer/panelkit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244822620,"owners_count":20516138,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["home-assistant","homeassistant","tileboard"],"created_at":"2024-10-12T03:46:02.594Z","updated_at":"2025-03-21T15:31:10.522Z","avatar_url":"https://github.com/dcramer.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# panelkit\n\nPanelKit is a tablet-focused UI built on top of Home Assistant.\n\nIt's heavily inspired by [tileboard](https://github.com/resoai/TileBoard), but built using a modern React stack.\n\n![PanelKit](/screenshots/example.png)\n\n## Table of Contents\n\n\u003c!--ts--\u003e\n\n- [panelkit](#panelkit)\n  - [Table of Contents](#table-of-contents)\n  - [Roadmap](#roadmap)\n  - [Config](#config)\n  - [Tiles](#tiles)\n    - [AlarmTile](#alarmtile)\n    - [AutomationTile](#automationtile)\n    - [CameraTile](#cameratile)\n    - [ClimateTile](#climatetile)\n    - [DoorControlTile](#doorcontroltile)\n    - [FanTile](#fantile)\n    - [InputSelectTile](#inputselecttile)\n    - [LightTile](#lighttile)\n    - [LockTile](#locktile)\n    - [SceneTile](#scenetile)\n    - [SensorTile](#sensortile)\n    - [ScriptTile](#scripttile)\n    - [SwitchTile](#switchtile)\n  - [Custom Tiles](#custom-tiles)\n  - [Development](#development)\n    - [Chrome Profiles](#chrome-profiles)\n\n\u003c!-- Added by: dcramer, at: Fri May 15 10:00:40 PDT 2020 --\u003e\n\n\u003c!--te--\u003e\n\n## Roadmap\n\nSome scribbles about what (hopefully) still needs done.\n\n- Grid system needs work. It'd ideally automatically layout with a fixed width grid, and variable unit-sized tiles. Tiles can live in groups, which then cascades the grid. e.g. traditional CSS grid systems, but with a bit more control.\n\n- Implement mobile-specific styles and metadata. A bit of work is done here, but we may still want to rethink the grid. Also need to finish up support for a standard portrait display (e.g. iPhone).\n\n- Needs thorough testing on mobile devices and tablets. Haven't done this at all yet, though some of the behavior is already implemented (for e.g. touch controls).\n\n- Revisit a number of design elements (like the Slider) to optimize for touch devices and smaller form factors. Some controls are likely going to be too small to tap easily, or need to respond faster to human interactions. A bunch of work is already done to make it _feel_ fast, but it's not complete yet.\n\n- Implement missing Tile features. Notes are generally inline in the Tile list below. To some degree I'm modeling feature parity with Tileboard, but there may be things that simply aren't worth porting (such as the device tracker) unless someone else opts to do it.\n\n- Consider migrating to `home-assistant-js-websocket`. I didn't notice it when I started development, and its got quite a lot of functionality/complexity that migration may or may not be worth it.\n\n- Add validation for config so that it doesn't throw cryptic javascript errors when e.g. you pass an invalid `type`.\n\n- Camera modal/door modal will need portrait-focused improvements.\n\n## Config\n\nConfiguration is read from `./config.js` - relative to your `index.html` file.\n\nThe `config.js` file must export `CONFIG` with the following baseline values:\n\n```javascript\n/* global TILE, React */\n\nwindow.CONFIG = {\n  url: \"http://localhost:8123\",\n  accessToken: \"your.long-lived.access-token\",\n  tiles: [],\n};\n```\n\nTiles are defined either as a nested tree of additional tiles (grouped together):\n\n```javascript\n{\n  // ...\n  tiles: [\n    {\n      tiles: [\n        {\n          type: TILE.LIGHT,\n          entityID: \"light.cool_light\",\n        },\n        // ...\n      ],\n    },\n  ];\n}\n```\n\nOo individual tiles themselves (see docs below for more details):\n\n```javascript\n{\n  // ...\n  tiles: [\n    {\n      type: TILE.LIGHT,\n      entityID: \"light.cool_light\",\n    },\n    // ...\n  ];\n}\n```\n\nGroups and individual tiles can be meshed together as needed to create your perfect layout.\n\n## Tiles\n\nEvery tile is a React component. A few common attributes are shared within configuration:\n\n\u003cdl\u003e\n  \u003cdt\u003e\u003ccode\u003etype\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eThe type attribute is the component that will be rendered. The \u003ccode\u003eTILE\u003c/code\u003e constant is a registry of default components.\u003c/dd\u003e\n\n  \u003cdt\u003e\u003ccode\u003etitle\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eThe title (or name) to make visible for the tile. Inferred from the entity when available.\u003c/dd\u003e\n\n  \u003cdt\u003e\u003ccode\u003esubtitle\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eAn optional subtitle to make visible for the tile.\u003c/dd\u003e\n\n  \u003cdt\u003e\u003ccode\u003eentityId\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eRequired by some tiles, this will couple the state of the tile to the given entity.\u003c/dd\u003e\n\n  \u003cdt\u003e\u003ccode\u003eicons\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eOptional mapping of icons to react to state changes.\u003c/dd\u003e\n\n  \u003cdt\u003e\u003ccode\u003eicon\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eOptional icon component to override the default (and the fallback when no other icon is available).\u003c/dd\u003e\n\n  \u003cdt\u003e\u003ccode\u003estate\u003c/code\u003e\u003c/dt\u003e\n  \u003cdd\u003eOptional mapping of states for labels\u003c/dd\u003e\n\n\u003c/dl\u003e\n\nAn example basic tile configuration:\n\n```javascript\n{\n  type: TILE.LIGHT,\n  entityId: \"light.string_lights_front\",\n  title: \"Christmas Lights\",\n  subtitle: \"Front Yard\",\n  icons: {\n    \"on\": \"string-lights\",\n    \"off\": \"string-lights-off\"\n  },\n  states: {\n    \"on\": \"Really On\",\n    \"on\": \"Really Off\",\n  }\n}\n```\n\nAnd with that, you'll get a basic light tile control with custom icons:\n\n![Light on](/screenshots/light-on.png) ![Light off](/screenshots/light-off.png)\n\n### AlarmTile\n\nA basic alarm control, showing the current state of the alarm and allowing disarm, armed_home, armed_away, and armed_night state changes.\n\n![AlarmTile](/screenshots/alarm.png)\n\n```javascript\n{\n  type: TILE.ALARM,\n  // The `alarm_control_panel` entity ID.\n  entityId: \"\",\n}\n```\n\n### AutomationTile\n\nA tile which to trigger an automation\n\n![AutomationTile](/screenshots/automation.png)\n\n```javascript\n{\n  type: TILE.AUTOMATION,\n  // The `automation` entity ID.\n  entityId: \"\",\n  // (Optional) The action to perform: toggle (default), trigger, turn_on, and turn_off\n  action: \"toggle\"\n}\n```\n\n### CameraTile\n\nA still capture of a camera, refreshed every few seconds. A single press goes into a full screen video, which will progressively load a streaming gif and video feed when possible. It also gives quick access to any other defined camera entities.\n\n![CameraTile](/screenshots/camera.png)\n\n```javascript\n{\n  type: TILE.CAMERA,\n  // The `camera` entity ID.\n  entityId: \"\",\n  // (Optional) The refresh interval for the camera still (in milliseconds). Defaults to `3000` (3s).\n  refreshInterval: 3000,\n  // (Optional) List of cameras to show. Defaults to all cameras with active tiles.\n  cameraList: [\"camera.name\"]\n}\n```\n\nThe modal allows cycling through camera feeds:\n\n![CameraModal](/screenshots/camera-modal.png)\n\n### ClimateTile\n\n**NOT FINISHED**\n\nA tile designed for climate control. A single press opens climate configuration.\n\n![ClimateTile](/screenshots/climate.png)\n\n```javascript\n{\n  type: TILE.CLIMATE,\n  // The `climate` entity ID.\n  entityId: \"\",\n}\n```\n\nTODO:\n\n- Add modal for advanced climate controls\n\n### DoorControlTile\n\n**NOT FINISHED**\n\nA tile designed for a door control system, including a main camera feed as well as various actions. A single press opens the full screen camera feed with door controls.\n\n![DoorControlTile](/screenshots/door-control.png)\n\n```javascript\n{\n  type: TILE.DOOR_CONTROL,\n  // The `camera` entity ID.\n  camerra: \"\",\n}\n```\n\nTODO:\n\n- Add controls within panel.\n- Add event-based prompt to load DoorControlModal.\n\n### FanTile\n\nA simple fan switch. A single press toggles the fan, a long press brings up a speed control.\n\n![FanTile](/screenshots/fan.png)\n\n```javascript\n{\n  type: TILE.FAN,\n  // The `fan` entity ID.\n  entityId: \"\",\n}\n```\n\nTODO:\n\n- Add speed control\n\n### InputSelectTile\n\nAn input selector. A single press activates the next option. A long press brings up a selector.\n\n![InputSelectTile](/screenshots/input-select.png)\n\n```javascript\n{\n  type: TILE.INPUT_SELECT,\n  // The `input_select` entity ID.\n  entityId: \"\",\n}\n```\n\nTODO:\n\n- Add long press widget\n\n### LightTile\n\nA simple light switch. A single press toggles the light, a long press brings up a brightness control.\n\n![LightTile](/screenshots/light.png)\n\n```javascript\n{\n  type: TILE.LIGHT,\n  // The `light` entity ID.\n  entityId: \"\",\n}\n```\n\nTODO:\n\n- Add RGB controls\n- Change portrait/small device display to be vertical sliders\n\n### LockTile\n\nA simple lock swtich. A single press toggles the lock.\n\n![LockTile](/screenshots/lock.png)\n\n```javascript\n{\n  type: TILE.LOCK,\n  // The `lock` entity ID.\n  entityId: \"\",\n}\n```\n\n### SceneTile\n\nA tile which can be pressed to activate a scene.\n\n![SceneTile](/screenshots/scene.png)\n\n```javascript\n{\n  type: TILE.SCENE,\n  // The `scene` entity ID.\n  entityId: \"\",\n}\n```\n\n### SensorTile\n\nA tile which displays the result of a sensor.\n\n![SensorTile](/screenshots/alarm.png)\n\n```javascript\n{\n  type: TILE.SENSOR,\n  // The `sensor` entity ID.\n  entityId: \"\",\n  // (Optional) A function to format the value.\n  format: function(state, attributes, unitOfMeasurement) { return state + ' ' + unitOfMeasurement }\n}\n```\n\n### ScriptTile\n\nA tile which can be pressed to activate a script.\n\n![ScriptTile](/screenshots/script.png)\n\n```javascript\n{\n  type: TILE.SCRIPT,\n  // The `script` entity ID.\n  entityId: \"\",\n  // (Optional) The payload to send along with the script.\n  data: {\n    // ...\n  }\n}\n```\n\nAn example using a custom Pi-hole script:\n\n![ScriptTile](/screenshots/script-custom.png)\n\n```javascript\n{\n  type: TILE.SCRIPT,\n  entityId: \"switch.pi_hole\",\n  icon: \"pi-hole\",\n}\n```\n\n### SwitchTile\n\nA simple switch. A single press toggles the switch.\n\n```javascript\n{\n  type: TILE.SWITCH,\n  // The `switch` entity ID.\n  entityId: \"\",\n}\n```\n\n## Custom Tiles\n\nYMMV. Enter at your own risk.\n\nA tile is just a component. So you can use React's APIs if you want to build your own:\n\n```javascript\n{\n  type: function (props) {\n    var e = React.createElement;\n\n    return e(\"div\", null, \"Example style-less widget\");\n  },\n},\n```\n\nRealistically if you go down this route, you're going to want to just spin up a simple application that can generate a CSS bundle with React components in it. You might want to look at create-react-app for this. From there you'll want to import PanelKit's `Tile` component and use it as defined in our built-ins.\n\nNote: The above is likely not possible today, but if this project gets use, we could easily abstract the tile components into their own module so folks could achieve this.\n\n## Development\n\nPanelKit is built using [`create-react-app`](https://github.com/facebook/create-react-app), and you'll find your standard helper scripts available.\n\nInstall dependencies:\n\n```shell\nyarn install\n```\n\nRun the development server:\n\n```shell\nyarn start\n```\n\nCreate `.env` to hold your credentials:\n\n```bash\n# .env\n# see `.env.example` for more information\n\nREACT_APP_HASS_URL=http://localhost:8123\nREACT_APP_HASS_ACCESS_TOKEN=\n```\n\nIn development we're currently reading config from `public/config.js`, which is version controlled and not ideal.\n\n### Chrome Profiles\n\nYou can use [mydevice.info](https://mydevice.info) to determine device specs, which is useful for mobile device testing.\n\n\u003cdl\u003e\n  \u003cdt\u003eGalaxy Tab A 10.5\"\u003c/dt\u003e\n  \u003cdd\u003e1280px x 800px, 80em, 1.5 pixel ratio\u003c/dd\u003e\n\u003c/dl\u003e\n\nYou may also want to disable context menu when testing against mobile profiles. This can be done in your Chrome console:\n\n```javascript\nwindow.oncontextmenu = function () {\n  return false;\n};\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcramer%2Fpanelkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdcramer%2Fpanelkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcramer%2Fpanelkit/lists"}