{"id":15385031,"url":"https://github.com/manekinekko/cafy","last_synced_at":"2025-04-15T19:43:41.609Z","repository":{"id":55040443,"uuid":"315117627","full_name":"manekinekko/cafy","owner":"manekinekko","description":"An experimental node package to interact with the Delonghi Primadonna Elite (and probably other ECAM models)","archived":false,"fork":false,"pushed_at":"2021-08-18T08:13:48.000Z","size":503,"stargazers_count":55,"open_issues_count":4,"forks_count":8,"subscribers_count":13,"default_branch":"main","last_synced_at":"2025-03-29T00:25:34.660Z","etag":null,"topics":["ble","coffee-machine","hacktoberfest","hacktoberfest-accepted","iot","nodejs"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/manekinekko.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-11-22T19:39:58.000Z","updated_at":"2025-01-11T12:20:16.000Z","dependencies_parsed_at":"2022-08-14T09:50:45.582Z","dependency_job_id":null,"html_url":"https://github.com/manekinekko/cafy","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manekinekko%2Fcafy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manekinekko%2Fcafy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manekinekko%2Fcafy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manekinekko%2Fcafy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/manekinekko","download_url":"https://codeload.github.com/manekinekko/cafy/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249141526,"owners_count":21219507,"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":["ble","coffee-machine","hacktoberfest","hacktoberfest-accepted","iot","nodejs"],"created_at":"2024-10-01T14:43:52.486Z","updated_at":"2025-04-15T19:43:41.588Z","avatar_url":"https://github.com/manekinekko.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Cafy\n\nThis is an experimental Node.js app to interact with the Primadonna Elite (ECAM65075MS) coffee smart machines (and probably other ECAM models), using the Bluetooth Low Energy (BLE) protocol.\n\n### Disclaimer\n\nI own a Primadonna Elite (ECAM 650.75.MS) and I created this app for my personal use. **The code shared in this project is for educational purposes only.**\n\nThe code 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 I (Wassim Chegham) 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 code or the use or other dealings in the code.\n\n# Prerequisites\n\nYou will need:\n\n- [Node v15 and npm](https://nodejs.org/en/download/current/)\n- [the git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) command line\n- [Code editor and a terminal](https://code.visualstudio.com/?WT.mc_id=javascript-00000-wachegha)\n- Make sure your computer has a Bluetooth adapter that supports [BLE](https://en.wikipedia.org/wiki/Bluetooth_Low_Energy).\n\n# Getting Started\n\n1. `git clone https://github.com/manekinekko/node-ecam-coffee.git`\n1. `cd node-ecam-coffee`\n1. `npm install`\n1. `npm start`\n\n## Sending commands (WIP)\n\nFor the time being, you can just edit the [index.ts](src/index.ts) file. We will provide a cleaner public API in the future.\n\n```typescript\nimport { App } from \"./classes\";\n\n(async function () {\n  let app = new App();\n  await app.sendCommand([\"0d 08 83 f0 02 02 06 c4 b1\"]);\n})();\n\n\u003e NOTE: you will need to rebuild your code (`npm start`) every time you change a file under [src](src/) folder.\n\n```\n\n## Project Status\n\nThis project is still under development. Most of the work is being invested in understanding the communication protocol and decoding the packets coming from the machine (see [src/decoder.ts](src/decoder.ts)).\n\n### Beverages\n\n| Beverage                                   | Trigger Command                                            | Stop Command                 |\n| ------------------------------------------ | ---------------------------------------------------------- | ---------------------------- |\n| Coffee                                     | `0d 0f 83 f0 02 01 01 00 67 02 02 00 00 06 77 ff`          | `0d 08 83 f0 02 02 06 c4 b1` |\n| Doppio+                                    | `0d 0d 83 f0 05 01 01 00 78 00 00 06 c4 7e`                | `0d 08 83 f0 05 02 06 41 21` |\n| Steam                                      | `0d 0d 83 f0 11 01 09 03 84 1c 01 06 c0 7b`                | `0d 08 83 f0 11 02 06 de 82` |\n| Hot Water                                  | `0d 0d 83 f0 10 01 0f 00 fa 1c 01 06 04 b4`                | `0d 08 83 f0 10 02 06 e9 b2` |\n| x2 Espresso                                | `0d 0f 83 f0 04 01 01 00 28 02 02 00 00 06 ab 53`          | `0d 08 83 f0 04 02 06 76 11` |\n| Americano                                  | `0d 12 83 f0 06 01 01 00 28 02 03 0f 00 6e 00 00 06 47 8b` | `0d 08 83 f0 06 02 06 18 71` |\n| Coffee Long                                | `0d 0f 83 f0 03 01 01 00 a0 02 03 00 00 06 18 7f`          | `0d 08 83 f0 03 02 06 f3 81` |\n| Espresso x1 (Aroma=3 Temperature=2 Qty=40) | `0d 11 83 f0 01 01 01 00 28 02 03 08 00 00 00 06 8f fc`    | `0d 08 83 f0 01 02 06 9d e1` |\n\n### Machine settings\n\n| Setting           | Trigger Command                       |\n| ----------------- | ------------------------------------- |\n| Turn on           | `0d 07 84 0f 02 01 55 12`             |\n| Cup Light On      | `0d 0b 90 0f 00 3f 00 00 00 99 39 22` |\n| Cup Light Off     | `0d 0b 90 0f 00 3f 00 00 00 91 b8 2a` |\n| Cup Warmer On     | `0d 0b 90 0f 00 3f 00 00 00 b1 9c 48` |\n| Cup Warmer Off    | `0d 0b 90 0f 00 3f 00 00 00 91 b8 2a` |\n| Energy Saving On  | `0d 0b 90 0f 00 3f 00 00 00 91 b8 2a` |\n| Energy Saving Off | `0d 0b 90 0f 00 3f 00 00 00 81 aa 1b` |\n| Beep Sound On     | `0d 0b 90 0f 00 3f 00 00 00 91 b8 2a` |\n| Beep Sound Off    | `0d 0b 90 0f 00 3f 00 00 00 95 f8 ae` |\n| Show Time         | `0d 08 95 0f 00 5f 03 00 eb`          |\n| Water Hardness 1  | `0d 0b 90 0f 00 32 00 00 00 00 0a c8` |\n| Water Hardness 2  | `0d 0b 90 0f 00 32 00 00 00 02 2a 8a` |\n| Water Hardness 3  | `0d 0b 90 0f 00 32 00 00 00 02 2a 8a` |\n| Water Hardness 4  | `0d 0b 90 0f 00 32 00 00 00 03 3a ab` |\n\n### Other\n\n|                               |                       |\n| ----------------------------- | --------------------- |\n| Beverage Statistics           | TODO                  |\n| Profiles                      | TODO                  |\n| \"My\" Custom Beverage settings | TODO                  |\n| Monitor data                  | WIP                   |\n| Decoding machine responses    | [WIP](src/decoder.ts) |\n\n# Protocol\n\n## Request/Response Packet Format\n\n### Request Packet\n\n```text\n  00              01                      N               n-1                n\n+----+----------------------------+---+---+---+---+------------------+----------------+\n| 0d |   request packet size (n)  |     data      |  checksum byte   | checksum byte  |\n+----+----------------------------+---+---+---+---+------------------+----------------+\n```\n\n### Response Packet\n\n```text\n  00              01                      N               n-1                n\n+----+----------------------------+---+---+---+---+------------------+----------------+\n| d0 |   request packet size (n)  |     data      |  checksum byte   | checksum byte  |\n+----+----------------------------+---+---+---+---+------------------+----------------+\n```\n\n### Checksum Algorithm\n\n```javascript\nlet deviser = 0x1d0f;\nfor (let byteIndex = 0; byteIndex \u003c bytes.length - 2; byteIndex++) {\n  let i3 =\n    (((deviser \u003c\u003c 8) | (deviser \u003e\u003e\u003e 8)) \u0026 0x0000ffff) ^\n    (bytes[byteIndex] \u0026 0xffff);\n  let i4 = i3 ^ ((i3 \u0026 0xff) \u003e\u003e 4);\n  let i5 = i4 ^ ((i4 \u003c\u003c 12) \u0026 0x0000ffff);\n  deviser = i5 ^ (((i5 \u0026 0xff) \u003c\u003c 5) \u0026 0x0000ffff);\n}\nchecksum = deviser \u0026 0x0000ffff;\n```\n\nExample: `bytes=[0d 05 75 0f da 25]`\n\n1. for all bytes except the last 2 bytes: `0d 05 75 0f`\n   1. let i3 = (((i \u003c\u003c 8) | (i \u003e\u003e\u003e 8)) \u0026 0x0000ffff) ^ (bytes[0] \u0026 0xffff);\n      1. 0x1d0f \u003c\u003c 8 = 0x1d0f00 (1904384)\n      1. 0x1d0f \u003e\u003e\u003e 8 = 0x1d (29)\n      1. (0x1d0f00 | 0x1d) = 0x1d0f1d (1904413)\n      1. (0x1d0f1d \u0026 0x0000ffff) = 0xf1d (3869)\n      1. (0xd0 \u0026 0xffff) = 0xd0 (-48)\n      1. 0xf1d ^ 0xd0 = 0xfcd (4045)\n   1. let i4 = i3 ^ ((i3 \u0026 0xff) \u003e\u003e 4);\n      1. 0xfcd \u0026 0xff = 0xcd\n      1. 0xcd \u003e\u003e 4 = 0xc\n      1. 0xfcd ^ 0xc = 0xfc1\n   1. let i5 = i4 ^ ((i4 \u003c\u003c 12) \u0026 0x0000ffff);\n      1. 0xfc1 \u003c\u003c 12 = 0xfc1000\n      1. 0xfc1000 \u0026 0x0000ffff = 0x1000\n      1. 0xfc1 ^ 0x1000 = 0x1fc1\n   1. i = i5 ^ (((i5 \u0026 0xff) \u003c\u003c 5) \u0026 0x0000ffff);\n      1. 0x1fc1 \u0026 0xff = 0xc1\n      1. 0xc1 \u003c\u003c 5 = 0x1820\n      1. 0x1820 \u0026 0x0000ffff = 0x1820\n      1. 0x1fc1 ^ 0x1820 = 0x7e1\n\n## Monitoring Data\n\n### Request Packet\n\n```text\n  00   01   02   03   04   05\n+----+----+----+----+----+----+\n| 0d | 05 | 75 | 0f | da | 25 |\n+----+----+----+----+----+----+\n\n00= Request magic byte\n01= Request packet size\n02= Monitoring data type (T0=0x70, T1=0x75, T2=0x75)\n03= 0x0f (??)\n04= Checksum byte\n05= Checksum byte\n```\n\n### Response Packet\n\n```text\n  00   01   02   03   04   05   06   07   08   09   10   11   12   13   14   15   16   17   18\n+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+\n| d0 | 12 | 75 | 0f | 01 | 01 | 00 | 08 | 00 | 00 | 02 | 00 | 00 | 00 | 00 | 00 | 00 | 7d | 05 |\n+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+\n\n00= Response magic byte\n01= Response packet size\n02= Operation ID\n03= ??\n04= Accessory Present (00: no accessory, 01: water spout, 02: milk spout, 03: chocolate spout, 04: milk clean dial)\n05= ??\n06= ??\n07= ??\n08= ??\n09= ??\n10= Machine Model Id\n11= Dispensing Percentage\n12= ??\n13= ??\n14= ??\n15= Main Board Software Release\n16= ??\n17= Checksum byte\n18= Checksum byte\n```\n\n### Rinsing process\n\n```\nStatus:\n  Decoder       - InStandBy             = false\n  Decoder       - TurningOn             = false\n  Decoder       - ReadyToWork           = false\n  Decoder       - ShuttingDown          = false\n  Decoder       - ShutDown              = false\n  Decoder       -------------------------\n  Decoder       - AccessoryPresent      = 1\n  Decoder       - ActiveAlarms          = 2,11\n  Decoder       - BeverageType          = undefined\n  Decoder       - CoffeeInfuserPos      = 0\n  Decoder       - CoffeePowderQty       = -1\n  Decoder       - CoffeeWasteCounter    = undefined\n  Decoder       - DispensingPercentage  = 0\n  Decoder       - FunctionOngoing       = 4\n  Decoder       - HeaterTemp            = undefined\n  Decoder       - MachineModelId        = -1\n  Decoder       - MainBoardSwRelease    = 0\n  Decoder       - OnLoads               =\n  Decoder       - OnSwitches            = 0,2\n  Decoder       - OnSwitchesToShowUser  =\n  Decoder       - PressedKeys           =\n  Decoder       - RequestedWaterQty     = -1\n  Decoder       - SteamerTemp           = undefined\n  Decoder       - Timestamp             = undefined\n  Decoder       - Type                  = 2\n  Decoder       - Value                 = [d0 12 75 0f 01 05 00 04 08 04 09 00 00 00 00 00 00 fa 12] (19)\n  Decoder       - WaterFlowQty          = 4608\n```\n\n## Troubleshooting\n\n#### Running on Linux/RPi (EPERM, Operation not permitted)\n\nIf you get the following error:\n\n```text\nhci onSocketError: EPERM, Operation not permitted\nnoble warning: adapter state unauthorized, please run as root or with sudo\n               or see README for information on running without root/sudo:\n               https://github.com/sandeepmistry/noble#running-on-linux\n```\n\nYou need to apply the following command:\n\n```bash\nsudo setcap cap_net_raw+eip $(eval readlink -f `which node`)\n```\n\nMore info here: https://github.com/noble/noble#running-on-linux\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanekinekko%2Fcafy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmanekinekko%2Fcafy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanekinekko%2Fcafy/lists"}