diff --git a/package.json b/package.json index 89c5db7..e892778 100644 --- a/package.json +++ b/package.json @@ -9,25 +9,29 @@ "@johntalton/adxl375": "^1.0.0", "@johntalton/aht20": "^1.2.0", "@johntalton/am2320": "^1.0.0", - "@johntalton/and-other-delights": "../and-other-delights", + "@johntalton/and-other-delights": "^8.3.0", "@johntalton/aw9523": "^2.0.0", "@johntalton/bitsmush": "^1.0.1", "@johntalton/boschieu": "^6.0.1", "@johntalton/ds1841": "^2.0.0", "@johntalton/ds3231": "^1.1.1", "@johntalton/ds3502": "^5.0.0", - "@johntalton/excamera-i2cdriver": "../excamera-i2cdriver", - "@johntalton/ht16k33": "../ht16k33", - "@johntalton/i2c-bus-excamera-i2cdriver": "../i2c-bus-excamera-i2cdriver", - "@johntalton/i2c-bus-mcp2221": "../i2c-bus-mcp2221", + "@johntalton/excamera-i2cdriver": "^3.0.0", + "@johntalton/ht16k33": "^1.1.1", + "@johntalton/i2c-bus-excamera-i2cdriver": "^3.0.1", + "@johntalton/i2c-bus-mcp2221": "^4.0.0", "@johntalton/i2c-bus-tca9548a": "^1.0.1", - "@johntalton/i2c-port": "^1.0.0", - "@johntalton/mcp2221": "../mcp2221", + "@johntalton/i2c-port": "^4.0.0", + "@johntalton/mcp2221": "^4.0.0", "@johntalton/mcp23": "^6.0.2", - "@johntalton/pca9536": "../pca9536", - "@johntalton/pcf8523": "../pcf8523", + "@johntalton/pca9536": "^1.0.2", + "@johntalton/pcf8523": "^2.0.1", "@johntalton/pcf8574": "^2.1.0", - "@johntalton/tca9548a": "^5.0.0", - "@johntalton/tsl2591": "^1.0.0" + "@johntalton/tca9548a": "^5.1.1", + "@johntalton/tsl2591": "^1.0.0", + "body-parser": "^1.20.2", + "cors": "^2.8.5", + "express": "^4.19.2", + "node-hid": "^3.1.0" } } diff --git a/public/css/app/app-main.css b/public/css/app/app-main.css index 90f3f92..7e3bcd9 100644 --- a/public/css/app/app-main.css +++ b/public/css/app/app-main.css @@ -38,6 +38,26 @@ main > section:not([data-active]) { display: none; } +[data-no-support] { + margin-inline: 0 2em; + margin-block-end: 1em; + padding: 1em; + border-radius: 1em; + padding-block: 2em; + + border: 7px groove var(--color-accent--darker-error, red); + background-color: var(--color-accent--lightest-error, red); + color: var(--color-accent--lightest-error-text, red); +} + +[data-no-support][data-dismissed] { + display: none; +} + +body:not([data-supports=""]) [data-no-support] { + display: none; +} + main > section { & p[data-loading] { display: flex; diff --git a/public/css/defs.css b/public/css/defs.css index 892940f..ddf5a60 100644 --- a/public/css/defs.css +++ b/public/css/defs.css @@ -28,6 +28,10 @@ [data-theme] { + --color-white: white; + --color-black: black; + --color-gray: gray; + /* */ --color-disabled: var(--color-gray); --color-opaque-black: rgba(50 50 50 / 0.25); diff --git a/public/css/libs/form.css b/public/css/libs/form.css index 3c177c8..8fb3b7b 100644 --- a/public/css/libs/form.css +++ b/public/css/libs/form.css @@ -128,19 +128,27 @@ form { & input[type="text"] { accent-color: var(--color-accent--darker, red); - border: 0; + /* background-color: var(--color-accent--lightest, red); + color: var(--color-accent--lightest-text, red); */ + background-color: var(--color-accent--lighter, red); + color: var(--color-accent--lighter-text, red); - border-radius: 2em; + border-width: 2px; + border-style: solid; + border-color: var(--color-accent--darker, red); + + border-radius: 1em; padding-inline-start: 1em; - padding-block: .5em; + padding-block: .75em; - &:not(:focus-visible) { - background-color: var(--color-accent--lighter, red); - color: var(--color-accent--lighter-text, red); + width: 100%; + + &:focus-visible { + background-color: var(--color-white, white); + color: var(--color-black, black); } &:disabled { - border: 0; background-color: #00000014; } diff --git a/public/custom-elements/web.html b/public/custom-elements/web.html new file mode 100644 index 0000000..d8bd2cb --- /dev/null +++ b/public/custom-elements/web.html @@ -0,0 +1,56 @@ + + + + + + +
+
+ + +
+
+ +
+ + + + + + + +
+
+ + \ No newline at end of file diff --git a/public/devices-i2c/adt7410.js b/public/devices-i2c/adt7410.js index dc90c07..1a55dd5 100644 --- a/public/devices-i2c/adt7410.js +++ b/public/devices-i2c/adt7410.js @@ -74,7 +74,9 @@ export class ADT7410Builder { // const refreshId = async () => { + // console.log('refresh id') this.#id = await this.#device.getId() + .then(id => { console.log(id); return id }) .catch(e => ({ manufactureId: NaN, revisionId: NaN, matchedVendor: false })) idOutput.value = `Manufacture: 0x${this.#id.manufactureId.toString(16).padStart(2, '0')}, Revision ${this.#id.revisionId} ${this.#id.matchedVendor ? '' : '🛑'}` diff --git a/public/devices-i2c/tsl2591.js b/public/devices-i2c/tsl2591.js index eacfdb4..75edb44 100644 --- a/public/devices-i2c/tsl2591.js +++ b/public/devices-i2c/tsl2591.js @@ -84,6 +84,7 @@ export class TSL2591Builder { check('enabled', enabled) check('powerOn', powerOn) + console.log({ gain, time }) const gainSelect = root?.querySelector('select[name="gain"]') gainSelect.value = gain @@ -278,7 +279,9 @@ export class TSL2591Builder { yield device.getColor() } catch(e) { - // console.log('again?', e) + console.log('break', e) + break + console.log('again?', e) await delayMs(1000 / 10) } } diff --git a/public/devices-serial/exc-i2cdriver.js b/public/devices-serial/exc-i2cdriver.js index e9d10e5..1be63e7 100644 --- a/public/devices-serial/exc-i2cdriver.js +++ b/public/devices-serial/exc-i2cdriver.js @@ -326,6 +326,13 @@ export class ExcameraI2CDriverUIBuilder { // bufferSize: 1 }) + const signals = await this.#port.getSignals() + console.log(`Clear To Send: ${signals.clearToSend}`) + console.log(`Data Carrier Detect: ${signals.dataCarrierDetect}`) + console.log(`Data Set Ready: ${signals.dataSetReady}`) + console.log(`Ring Indicator: ${signals.ringIndicator}`) + + // device author provided init script await initScript(this.#port) diff --git a/public/hydrate/theme.js b/public/hydrate/theme.js index 5868b6a..386c69a 100644 --- a/public/hydrate/theme.js +++ b/public/hydrate/theme.js @@ -14,8 +14,13 @@ export async function hydrateTheme() { themRollerButton.setAttribute('title', theme) - const transition = document.startViewTransition(() => { + if (!document.startViewTransition) { document.body.setAttribute('data-theme', theme) - }) + } + else { + const transition = document.startViewTransition(() => { + document.body.setAttribute('data-theme', theme) + }) + } }) } \ No newline at end of file diff --git a/public/hydrate/ui.js b/public/hydrate/ui.js index c8e06ac..d175827 100644 --- a/public/hydrate/ui.js +++ b/public/hydrate/ui.js @@ -14,6 +14,8 @@ import { EXCAMERA_LABS_MINI_PRODUCT_ID } from '@johntalton/excamera-i2cdriver' import { asyncEvent } from '../util/async-event.js' +import { deviceGuessByAddress } from '../devices-i2c/guesses.js' +import { appendDeviceListItem } from '../util/device-list.js' const MCP2221_USB_FILTER = { @@ -113,7 +115,7 @@ export function buildDeviceListItem(deviceListElem, builder) { return } - const transition = document.startViewTransition(() => { + const transitionView = () => { deviceListElem.querySelectorAll(':scope > li').forEach(li => { li.removeAttribute('data-active') const bElem = li.querySelector('button') @@ -126,7 +128,15 @@ export function buildDeviceListItem(deviceListElem, builder) { mainElem.querySelectorAll(':scope > section').forEach(s => s.removeAttribute('data-active')) sectionElem.toggleAttribute('data-active', true) - }) + } + + if(!document.startViewTransition) { + transitionView() + } else { + const transition = document.startViewTransition(transitionView) + } + + }), { once: false }) // demolisher @@ -255,29 +265,42 @@ export async function addI2CDevice(definition) { export class I2CBusWeb { #url - constructor(url = 'http://localhost:3000/mcp') { + constructor(url = 'http://localhost:3000/port') { this.#url = url } + get name() { return `WebI²C(${this.#url})`} + async postCommand(command, options) { try { - const result = await fetch(this.#url, { + const response = await fetch(this.#url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ + namespace: window.origin, + opaque: '🤷🏻‍♂️', type: command, ...options }) }) - if(!result.ok) { throw new Error('result not ok') } + if(!response.ok) { throw new Error(`response not ok ${response.status}`) } + + const result = await response.json() + + if(result.type === 'error') { + throw new Error('WebI²C Remote Error: ' + result.why) + } - return result.json() + return { + ...result, + buffer: (result.buffer !== undefined) ? Uint8Array.from(result.buffer) : undefined + } } catch(e) { - console.warn('fetch exception', e) + // console.warn('fetch exception', e) throw e } } @@ -319,8 +342,105 @@ export class I2CBusWeb { } } -// addI2CDevice({ -// type: 'ht16k33', -// address: 0x71, -// bus: new I2CBusWeb() -// }) +addI2CDevice({ + type: 'ht16k33', + address: 0x71, + bus: new I2CBusWeb() +}) + +const deviceListElem = document.getElementById('deviceList') +const builder = { + title: '☠️', + open() { + + }, + async buildCustomView() { + const response = await fetch('./custom-elements/web.html') + if (!response.ok) { throw new Error('no html for view') } + const view = await response.text() + const doc = (new DOMParser()).parseFromString(view, 'text/html') + + const root = doc?.querySelector('web-config') + if (root === null) { throw new Error('no root for template') } + + const urlText = root.querySelector('input[name="url"]') + const scanButton = root.querySelector('button[data-scan]') + const deviceList = root.querySelector('[data-device-list]') + const addressElem = root.querySelector('addr-display[name="scanResults"]') + + scanButton?.addEventListener('click', asyncEvent(async event => { + scanButton.disabled = true + + const bus = new I2CBusWeb(urlText.value) + + const existingHexs = addressElem.querySelectorAll('hex-display') + existingHexs.forEach(eh => eh.remove()) + + const existingLis = root.querySelectorAll('li') + existingLis.forEach(el => el.remove()) + + + try { + const { addresses: ackedList } = await bus.scan() + + ackedList.forEach(addr => { + const acked = true + + + const hexElem = document.createElement('hex-display') + + hexElem.setAttribute('slot', addr) + + hexElem.toggleAttribute('acked', true) + // hexElem.toggleAttribute('arbitration', arbitration) + // hexElem.toggleAttribute('timedout', timedout) + + hexElem.textContent = addr.toString(16).padStart(2, '0') + + addressElem.append(hexElem) + + // + const listElem = document.createElement('li') + listElem.textContent = addr + + listElem.setAttribute('slot', 'vdevice-guess-list') + listElem.toggleAttribute('data-acked', true) + + const guesses = deviceGuessByAddress(addr) + const item = appendDeviceListItem(deviceList, addr, { acked, guesses }) + + item.button.addEventListener('click', e => { + e.preventDefault() + + // + item.button.disabled = true + const deviceGuess = item.select.value + + const controller = new AbortController() + const { signal } = controller + + UI_HOOKS.addI2CDevice({ + type: deviceGuess, + bus, + address: addr, + + port: undefined, + signal + }) + + + }, { once: true }) + + }) + } + catch(e) { + console.log(e) + } + + scanButton.disabled = false + })) + + return root + } +} +const demolisher = buildDeviceListItem(deviceListElem, builder) \ No newline at end of file diff --git a/public/index.html b/public/index.html index a1bb7b9..e50993a 100644 --- a/public/index.html +++ b/public/index.html @@ -59,7 +59,7 @@ - +

⚙️ Web I²C Playground

@@ -84,6 +84,11 @@

⚙️ Web I²C Playground

+
+ 😢 + No supported bus types + +